Chopper 2005

    A.Final Edition

    This is the final project of comp471 in the year of 2005. I don't quite remember why I refused to publish this good project.

    The only thing I can remember now is that there are several reasons that I felt not completely satisfied with it.

    a) I was a bit lost about my AI course and it was a painful experience when you think for all the time you are going to do

    something. However, things are not going very well.

    b) I was rejected by all graduate applications to other universities. This was a sad experience.

    c) For the first time, I was not the leader and organizer of an intensive programming project. I should admit the French

    guy is really a smart guy from EE who is completely organized, thoughtful, logical, realistic etc. Whatever the good property

    a computer science student lacks will be found in him. Am I a bit jealous? I guess so. Just a little bit.

    d) There are too much data in this project and the report is full of picture which makes them unsuitable for the shrinking

    free space of my web site.

    e) My drawing of helicopter is a kind of ugly. So, we decide to use his part of code and I just concentrate on the flight

    control and collision detection and all management of movement etc. Also he made an impressive good environment plus

    cube-textured sky and sound effects etc. So, my contribution in this project is not very much.

    However, recently I dig it up because of my "out-of-core-rendering" project which needs a "walk-through" engine. And I cannot

    find a better solution than my old pilot control system.

    B.The problem

    The best way to describe the project is to extract related parts from the final report. And I am in charge of those blue

    parts. Also I have to omit those nice pictures which is really a pity. However, if you like, you can download full project

    here.

    ¡¡

### 1.1.1 Environment

The environment simulates a battlefield on a snowy mountain. It consists of: a textured sky, a terrain with snow texture, evergreen trees, chopper base, buildings, and chopper missiles. Note that besides buildings and missiles, the whole environment was drawn uniquely using OpenGL primitives, texturing were used to add a more realistic look.

Sky: In 3D games, the sky is modeled usually as a big cube or sphere surface where the rest takes place inside the surface. In a shooting game, it is better to use a cube, because at any time it is easier to locate the scene limit. As The Hurricane, is a shooting game, the sky was modeled by a textured cube with a low number of polygons.

Terrain: we wanted to have a terrain with hills and valleys. This is known as terrain rendering. Possible implementations were:

¡¤ Nurbs and Bezier surfaces: it gives a detailed realistic rendered terrain. Using this technique made the processing of the game very slow, an unwanted disadvantage.

¡¤ Height maps: it gives a generic rendered terrain with fewer details than the above technique. Using height maps would have been a good choice, but in terms of coding it turned to be more complex and time was needed to understand and get familiar with this technique.

As time was a limiting factor and because it was not a priority, we decided to use a tessellated 3D grid with a texture that gives the feeling of a terrain with some hills and valleys.

Evergreen trees: to give the feeling of a snowy mountain battlefield. We decided to add evergreen trees onto the terrain. An easy idea was to draw it using three superimposed cones and a cylindrical trunk.

Chopper Base: the chopper base is where the game starts at with the chopper at rest on the ground. It was created by a square polygon with a texture overlapped upon its surface.

Buildings: Different then the remainder of the environment, the buildings were built using Maya 3D software, and later exported as an object file to incorporate them in the rest of the environment.

These objects were constructed using many different cubes, each transformed in a different size to display demolished buildings. By having these buildings displayed in such a manner, the environment illustrates a war zone and not a quiet wooded landscape. Therefore, they accentuate the surroundings by displaying a war torn landscape so that the user will have a better feeling for the game action.

However, once the building files had to be implemented in the remainder of the program, the loader would mention an error regarding the normal vector of the objects. With no time to fix the problem, the buildings were discarded from this version of the game.

Missiles: To simulate bullets fired from the chopper, missiles were implemented in the game. Similar to the buildings, they were built in Maya from primitive objects. They are formed of three different types of polygons; a sphere, a cylinder and several cubes. The cylinder was scaled to create the missiles body, the sphere was clipped to form the head and the different cubes were transformed to form the stabilizing wings.

### 1.1.1 Chopper

The chopper is a variation model of a Black Hawk helicopter. It consists of four pieces: cockpit, tail, rotors, and wheels. It was drawn completely with OpenGL objects and overlaid with two different textures. It has the z-axis as an axe of symmetry. The user can drive the chopper along the scene to track and shoot targets.

Cockpit: drawn using three scaled spheres, with appropriate clipping. The first sphere form the body of the cockpit, the second is the down-cover, last is the transparent window. Attached to the cockpit are two identical machine guns, each drawn using two cylinders, spheres, and a polygon to attach it to the cockpit.

Tail: Built using two cylinders with appropriate clipping, and a polygon to constitute the back rotor support.

Rotors: Main and tail rotors of the chopper are built with OpenGL polygons, two rectangles form two blades for each rotor.

In order to have a realistic view, motion blur was added for the rotors. Many techniques are used to implement motion blur. The most common technique is Accumulation Buffer, it consists of using a separate buffer to combine multiple renderings from the color buffer so to simulate motion blur. A disadvantage of the Accumulation Buffer technique is that is absorb a lot of resources thus slowing down the game.

Because of this severe pitfall, the Accumulation Buffer technique was discarded. Instead, we simulating the motion blur by drawing the blades several times with a small angle difference between successive blades and at each time a different degree of blending was applied. It resulted in a very good motion blur effect without slowing down the game.

Wheels: The chopper has two front and one back wheels, built by drawing a torus. The wheels are attached to the chopper via cylindrical legs.

### 1.1.1 Targets

In The Hurricane, tanks and war planes serve as invading enemy targets to be destroyed by the user controlling the chopper. These 3D targets have been primarily in Maya to make them look as realistic as possible because it will be the main focus of the user. Keep in mind that this is one of the few pieces where Maya was used in the project.

#### 1.1.1.1 Tank

Based on the Leopard 2A4 tanks, the game¡¯s tanks can be considered a variation of this military vehicle. They were created by transforming various primary objects in Maya and later piecing these polygons (cubes, spheres, cylinders and torus) together. Later one an appropriate texture was added in OpenGL.

The tank¡¯s body was created by transforming several cubes into rectangles and placing them together to display the tank¡¯s familiar shape.

Cannon: It was built from a cylinder that was scaled so that it can be integrated with the rest of the model¡¯s body structure without looking disproportionate. So that the end of the cannon has a large aperture, a torus was scaled and added at the very end of the large tube. The cannon is attached to the upper part of the tank¡¯s body.

Tracks: The two tracks were formed by scaling a torus. They are both placed under the tank, on both sides.

Wheels: The eight wheels were constructed by combining two different polygons together; a torus was used to form the rim and a sphere was used to form the center of the tire. The wheels are placed inside of the two tracks on either side of the tank. There are four wheels per track.

Based on the F-16 Fighting Falcon plane, the game¡¯s planes can be considered a variation of this military vehicle. Again having used several types of primitive objects such as spheres, cylinders and cubes in Maya, we later pieced these transformed polygons together to create a realistic version of these military planes. Later on an appropriate texture was added in OpenGL.

The plane¡¯s body is created by transforming a sphere to give it an ovular shape.

Cockpit: The plane¡¯s cockpit that is situated on the forward section of the body was created by a half sphere.

Tail: The tail is formed of several cubes also transformed to produce a proper rudder.

Wings: The wings which are situated on either side of the plane are created by transforming two different cubes which were later placed together to have the proper angle and shape of the wings. There is a set of wings attached to the plane¡¯s upper body at approximately its middle area and another set of wings attached to the plane¡¯s upper body at its back area.

Once the plane and tank where designed in Maya, they had to be exported as an Object file (.obj) so that they can later be implemented in OpenGL by embedding the file in the program¡¯s code. Only by doing so was it possible to add them into the remainder of the game.

### 1.1.1 Lighting and material variations

Parts of the graphics engine in this game are lighting and material variations. At any time the user can change the material variation to reflect a fogy or shiny scene. During the game, the scene changes automatically from day to night where the ambient light changes to reflect a night scene viewed through army night binocular. In the night scene, chopper light can be disabled/enabled.

Camera: It was essential to have a flexible and realistic control of flight in a shooting game. Otherwise player might be unable to detect those fast-moving targets. We supply third-person-view camera apart from first-person-view camera in order to help player re-locate from lost in flight and easily detect moving targets because without much sense of coordinate pilot can be easily loose sense of location. As the camera is closely connected with flight control, the algorithm will be introduced as following.

Flight Control: We supplied six degrees freedom of flight control. That is forward/backward with respect of current heading of chopper, left/right perpendicular to current heading of chopper, upward/downward perpendicular to current heading of chopper, yaw, pitch, roll. Therefore player can move in any arbitrary position and posture.

Design Approach: At beginning, we wanted to use similar idea of OpenGL function "gluLookAt" which use two vector plus one coordinate to represent both position and posture of chopper. However, this proves to be a bad choice because of two disadvantages:

a) You cannot take advantage of rotating functions in OpenGL and you have to calculate your own transformation matrix. By solving the cross product and dot product of vectors, we needed to solve a linear system and at some boundary cases we needed to consider the case of divided-by-zero situations. And it is quite a computation intensive approach for programmer. (Even though these calculation cannot be avoided in any approach, but it can be done by OpenGL and possibly be optimized.)

b) Another disadvantage is that trigonometry function is not linear. Therefore by using vector representation we cannot interpolate the transition by linearly modify vectors unless vector is transformed back to angles. The immediate result is loss of smoothness in flight control. This is why we returned to current design which uses angles plus coordinate.

Implementation: We defined a Movement structure which contains current coordinate of chopper plus angles of yaw, pitch and roll of chopper from original posture. However, in first-person-view camera it is not simply three rotations because the previous two rotations will have some effects on the third rotation. It is like this:

GLfloat LookAt_x=current_coord.x+ sin(yaw)*cos(pitch);

GLfloat LookAt_y= current_coord.y+sin(pitch)*cos(roll);

GLfloat LookAt_z= current_coord.z+cos(yaw)*cos(pitch);

In the assignment, we didn't solve this problem and we feel happy that we solve it in the project. This is one of many things that we¡¯ve learned in this project

.

Flight Control Implementation: Flight control became much easier after we achieved the correct representation of position and posture of chopper because the calculations are all similar and we used the same function for other repeatedly. For example, we defined a function "calcCoord" which calculates the relative coordinate offset with respect to current position of chopper when chopper moves a distance d in forward direction. It is like this:

x_offset=d*cos(pitch)*sin(yaw);

y_offset=d*sin(pitch)*cos(roll);

z_offset=d*cos(pitch)*cos(yaw);

Then all other calculations will be done by simply change the passing parameter.

backward: change distance d to -d

left/right: change yaw to yaw+90, yaw-90 respectively

up/down: change pitch to pitch+90, pitch-90 respectively

yaw/pitch/roll: just modify each respectively without calculation

Even the "chopper light" was also calculated by this manner.

## 1.1 Algorithms

For every interactive game and to implement what can look as straightforward features (detection collision, bullet trajectory, etc¡), many algorithms are needed.

### 1.1.1 Timing

Many features in computers games are based on timing. In The Hurricane, we used to timing to add a innovative feature which consist of an automatic switch between day and night scenery after a desired time have elapsed.

For the shooting, timing was especially essential for rocket flying track and collision detection. Therefore we needed to maintain record of timing in various data structures. For example, for rockets or bullets, moving targets, exploded flying fragments all need a starting moment be recorded in its data structure so that the position and posture can be updated in OpenGL display callback function.

### 1.1.2 Gun Fire Simulation and Collision Detection

The gun fire and collision detection algorithm contains three parts. These are rocket flight tracking, target tracking, collision detection, explosion simulation algorithms.

Rocket Flight Tracking: The rocket flight is exactly same as chopper flight control system except that each rocket must maintain its own starting data for position, posture and moment. Then these data will be updated whenever display callback is executed.

a) position: The position will be exact the position of chopper when the rocket is fired.

b) posture: Since we allow player to shoot at arbitrary angle by clicking mouse in first-person-view window, the starting posture of rocket will be slightly different from chopper when fired. By calculating the coordinate of mouse click, we can figure out the relative offset of mouse pointer against center of first-person-view which is regarded as current heading of chopper. Even though we understand it is not linear relationship between the offset of coordinate and current yaw, pitch and roll of chopper, user still can get a realistic vision because we only need an initial shooting direction for each rocket. The calculation of initial direction of rocket is like this:

angle_offset_yaw = horizontal_offset/ half_width_window * horizontal_view_angle;

angle_offset_pitch = vertical__offset / half_height_window * vertical_view_angle;

The vertical _view_angle is chosen as perspective view_angle in perspective view function. And

horizontal_view_angle = vertical_view_angle * (height_window/ width_window);

c) moment: It is the timing data acquired in environment by function "GetClickCount".

Then the rocket flight is exactly like the flight of chopper.

Target Tracking: In a real 3D shooting game, the target tracking would be very complicated since a possible AI might be involved. In our project, we choose a simple solution that all targets only move in a constant speed at a constant direction. Therefore to track all positions of targets, we can iterate all target initial position by adding an offset of elapsed_time*moving_speed to the direction of movement.

Collision Detection: Please refer to the below graph.

¡¡

The graph of collision detection.

¡¡

Figure SEQ Figure \* ARABIC 17 Collision detection: The arrow represents vector of direction of rocket flying and the direction from firing position to target

a) Design Approach: A naive collision detection algorithm is to simply calculate the immediate distance between each pair of flying rocket and target. However, the display callback function is executed in a discrete manner so that it is possible that display callback is called at moment when rocket is at position "near point" and "far point". At both position, the distance between rocket and target might be bigger than a pre-defined "explosion distance". But obviously the rocket can hit target between these two points. So, this naive algorithm won't work properly.

b) Implementation Details:

At any moment for any flying rocket with respect to any target, the collision detection algorithms all the same. We need to calculate the collision point and then compare the current position of rocket with it. If we take into consideration of movement of direction of target, the calculation is quite complicated. Therefore, we deliberately make the movement speed of target very small compared with moving speed of rocket so that we can ignore the factor of moving direction of target. However, still we consider the changing position of target at any moment.

The collision happens only when these two conditions are satisfied:

i) At some future moment, the shortest distance between target¡¯s position and the line of rocket flying is smaller than "explosion distance".

ii) The current position of flying rocket is equal to or exceeding this collision point.

The algorithm uses dot product to calculate the angle t between vector v1 representing rocket flying direction and vector v2 representing gun firing position to current target position. Therefore the distance d between firing position and collision point is:

d= dot(v1,v2); // v1 must be normalized first.

Then by checking the current flying time, we can know if rocket is equal to or exceeding the explosion point.

.

Explosion Simulation Algorithms: The explosion is always assumed to happen at the moment when rocket hits the target and at the position where the target is at that moment.

i) Flight Algorithms: Basically explosion is similar to rocket flying that as long as you set up the initial flying data. Every time when display callback is executed, the only thing you need to do is just updating the position.

ii) Initializing Flying Data: For a simple solution, we just used the position of target at the moment of explosion and randomly choosing the yaw, pitch and roll for each fragment of explosion.

iii) Fragments Representation: For a simple solution, we just used random sized, colored cube to represent each fragment as we have some problems in importing objects from Maya.

### 1.1.1 Sound Effects

One of the advanced features in The Hurricane is the presence of sound effects. It adds a lot of beauty to the game. The sound effects in The Hurricane are well synchronized: when the game load and before the user starts the chopper engine, a battle sound plays. When the user starts the chopper engine, rotors sound start playing continuously along with the rotors motion. The user can mute the rotors sound. Other sound effects are: the machine gun, plays every time the user presses the left-mouse click to shoot in a first-person view. Rooster sound plays automatically every time a new day starts; frog sound plays every time a new night starts. In addition to wind sound that plays in the middle of every day only if no other sound is playing, it adds a realistic feeling to the snowy mountain battlefield.

### 1.1.1 Code Structure

The code structure was divided into three parts:

¡¤ Texture and 3D loader engine: it implements the create texture function to create textures reading from a bitmap file, and loader function to load 3 object from .obj files.

¡¤ Environment engine: contains constant and macro definitions, abstract objects draw functions (sphere, etc¡). It also contains, several structures related to the game environment (Weather, DayTime, Sound, Camera, etc..) others related to targets, rockets and its motion, and functions to operate on these structures to be able to control different features in the game.

¡¤ Main file: Implements the game using the parts above. Contains draw functions for concrete objects in the game, main function, initialization and call back functions.

### 1.1.2 Code Performance

Except lighting, texture mapping, blending, we retained from any resource absorbing OpenGL technique (e.g. Nurbs, Bezier Curves). By using this code tuning, the game performance was acceptable.

## 1.1 User Commands

Like all computer games there are several commands that are integrated to allow the users to play the game. The navigation of the chopper depends on the specific commands that permit it to move in specific ways.

The commands that are included in the game which are associated to the navigation system of the chopper are the following:

¡¤ UP/DOWN arrows move the chopper forward or backwards

¡¤ LEFT/RIGHT arrows move the chopper at current right and left ¨C hand side directions

¡¤ y/Y permits the chopper to yaw

¡¤ p/P permits the chopper to pitch

¡¤ i/I and k/K permits you to move the chopper by rotating the center of its gravity

¡¤ u/U and d/D permits the chopper to move upwards and downwards vertically

¡¤ z/Z permits you to zoom in and out respectively

¡¤ By clicking on the mouse, when being in the first person perspective allows you to fire the missiles

¡¤ h/H controls the strength of light1¡¯s beam by choosing either a low or high beam.

¡¤ s/S permits you to turn on and off rotors

Many games are continuously played in the same perspective, being able to change to different views in which an individual can play in is an important aspect in 3D computer gaming. By doing so, the users can play the game viewing it in different angles. Please use these following commands to change view.

¡¤ o/O and n/N toggle between orthographic and perspective projections

¡¤ keys 1 and 3 permit you to switch between 1st and 3rd person perspective

¡¤ f/b you moves to the third person perspective and it¡¯s the camera that moves forward or backwards

¡¤ Keys 6 and 7 allows a rotation of the third person view camera around the chopper horizontally

¡¤ Keys 8 and 9 allows a rotation of the third person view camera around the chopper vertically

The environment in which the chopper is situated can also be manipulated by using another set of commands. These instructions help the individuals engaging in this chopper shooting game to have a better feel for the setting they are playing in. Here are these following commands;

¡¤ F4 permits you to toggle between the material properties of the chopper by choosing between a rusty or shiny appearance.

¡¤ F1 permits you to turn on and off the chopper light

¡¤ l/L permits you to turn on and off the sound effects in the game

¡¤ n/M permits you to mute or play rotors sounds

To facilitate the execution of this computer game, another set of commands where integrated so that the user exit gracefully and restart the game from the beginning at any point.

¡¤ esc key permits you to exit the game immediately

¡¤ C cheat key, if you are one of those who just want to finish the game and accomplish victory.

## 1.2 Short Walkthrough

The Hurricane is chopper shooting game may not necessarily be like other games of its nature. Staying in the same battlefield for an ultimate day and night combat, the user must control the chopper to destroy the many invading enemy tanks and planes coming his way. Only by destroying all of the targets will the individual win. To destroy the tanks and planes that come his way, aiming by the mouse cursor and shooting with the left-mouse click, the missiles must be launched at a point near the center of the vehicle.
¡¡
C.The idea of program
¡¡
In order to compile, you may need a library called "glaux.lib" and its "glaux.dll" for running. This is quite a standard
OpenGL auxiliary library which can be easily found in internet. Also glu and glut library will be needed.
¡¡
There are a huge number of data file like textures, sound files etc. Try to download the full source here.

    D.The major functions

    E.Further improvement

    F.File listing

    1. environment.h

    2. environment.cpp

    3. texture.h

    4. glm.h

    5. glm.cpp

    6. chopper.cpp

    ¡¡

    ¡¡

    file name: environment.h

    /***************************************************************************************************************/
    /* HEADER FILE FOR THE ENVIRONMENT.
    /*	Dated : Dec 9th 2005						Author: Elias Abou Zeid, e_abouze@ece.concordia.ca
    /*
    /*
    /*Notes and Acknowledgements:
    /**************************************************************************************************************/
    #ifndef ENVIRONMENT_H
    #define ENVIRONMENT_H

    #include <windows.h>

    # define FLOOR_SIZE 200.0 //floor size

    //window size
    #define  SIZE		1000.0

    //Projection related
    #define NEAR_Z 1.0
    #define FAR_Z 1000.0	// For the frustum size.

    #define PI 3.1415926535897932384626433832795

    // View Volume related
    //#define FIRSTVIEWFOVY  5.0
    #define FIRSTVIEWFOVY  60
    //#define FIRSTFVIEWVOL 0.07 //keep track of default values for first person view.
    #define FIRSTFVIEWVOL 20 //keep track of default values for first person view.
    #define FOVY  60
    #define FVIEWVOL 20.0 //keep track of default values for third person view.

    //Material color property, ambiant and diffuse light intensity
    # define HIGH		1.0
    # define LOW        0.5

    #define  HDAY       30000 //half day time in msec (30 sec)

    //size related
    #define FLOOR_LEVEL -2.5 //floor level
    //#define FLOOR_LEVEL 0 //-2.5 //floor level

    enum Projection {PERSPECTIVE, ORTHOGRAPHIC};

    enum DayTime {DAY, NIGHT};  //DAY (no light), NIGHT (controlabale lights)

    enum Weather {SHINY_DAY, FOGY_DAY};  //if it is a shiny or fogy day

    enum Camera {FIRST_PERSON, THIRD_PERSON}; //camera

    typedef struct{
    	LPCSTR filename;
    	bool mute;
    	float duration;
    } Sound;

    typedef struct {
    	Projection projection;
    	DayTime day;
    	Weather weather;
    	Camera camera;
    	GLfloat amdif; //ambiant diffuse light intensity
    	bool IsShiny; //toggle between shiny non-shiny material
    	DWORD timeday; // start time of a day
    	DWORD lastTickCount;
    } Environment;

    void drawSphere(float fRadius, GLint slices, GLint rings);
    							// Used to generate a Sphere shape.
    void drawCylinder(float fbaseRadius, float ftopRadius, float fHeight, GLint slices, GLint rings);
    							// Used to generate a cylinder shape.

    void drawTreeAt(GLfloat x, GLfloat z);
    void drawGroundTrees();
    void drawBaseAt(GLfloat x); //draw a cube base

    void drawParaBombAt(GLfloat x,GLfloat z);

    /////////////////////////////////////////////////////////////////////////

    const GLfloat HecLevel = FLOOR_LEVEL + 0.45; //the chopper level
    const GLfloat TargLevel = FLOOR_LEVEL + 5; //target level
    const GLfloat ExplosionDistance=1.5;

    const GLfloat DigitDisplayAngleOffset=5;
    const GLfloat DigitDisplayPositionOffset=2;

    const GLfloat MaxLatitude=70;
    const GLfloat FlyBoundary=FLOOR_SIZE/2-5;
    const GLfloat ChopperLength=2.5;
    const GLfloat MinLatitude=FLOOR_LEVEL+0.45;
    const GLfloat LightOnePitchOffset=-10;
    //const GLfloat Flight_Latitude=0.9;
    const GLfloat Flight_Latitude=TargLevel;
    //const GLfloat Flight_Latitude=HecLevel;

    const GLfloat AngleOffset=5;
    const GLfloat PositionOffset=1;
    const GLfloat MovementOffset=1;
    GLfloat deg2rad(GLfloat deg);
    GLfloat rad2deg(GLfloat rad);

    struct Movement
    {
    	GLfloat x,y,z;
    	GLfloat yaw, pitch, roll;
    };

    void initializeMovement();

    ///////////////////////////////////////////////////////////////////////////

    struct Rocket
    {
    	Movement firePos;
    	DWORD fireMoment;
    };

    ////////////////////////////////////////////

    const int MaxFragmentCount=9;
    const int MaxMissileModelCount=1;
    const int PictureTextureStartIndex=8;
    const int MaxSingleBuildingCount=10;
    const int MaxExplosionPieces=9;

    const int MaxRocketCount=60;
    const float RocketFlySpeed=0.01;
    const float RocketSize=0.01;
    const DWORD MaxRocketFlyTime=6000;

    const DWORD MaxFragmentFlyTime=20000;
    const float FragmentFlySpeed=0.005;

    void drawRockets(DWORD now);
    void drawRocket();
    bool calcRocket(int index, DWORD now, float &x, float &y, float &z);
    void calcCoord(float distance, const Movement& position, float &x, float &y, float &z);
    ////////////////////////////////////////////////////////////////////////
    const int MaxTargetCount=12;
    const float TargetMovementSpeed=0.001;
    //const float Explosion
    struct TargetStruct
    {
    	int coordIndex;
    	int modelIndex;
    };

    //////////////////////////////////////////////////////////////////////////

    #endif

    ¡¡

    file name: environment.cpp

    #include <glut.h>
    #include <stdio.h>
    #include <math.h>
    #include "Environment.h"

    /////////////////////////////////////////////////////////////////////

    extern GLfloat targetCoordLists[MaxTargetCount][3];
    Movement movement;
    int explosionIndex=-1;

    Rocket rockets[MaxRocketCount];
    Rocket explosions[MaxExplosionPieces];
    int explosionCount=0;

    int rocketCount=0;
    int targetCount=0;
    TargetStruct targets[MaxTargetCount];

    void calcCoord(float distance, const Movement& position, float &x, float &y, float &z)
    {
    	x=distance*cos(deg2rad(position.pitch))*sin(deg2rad(position.yaw));
    	y=distance*sin(deg2rad(position.pitch))*cos(deg2rad(position.roll));

    	z=distance*cos(deg2rad(position.pitch))*cos(deg2rad(position.yaw));
    }

    void normalize(float vect[3])
    {
    	float norm=0;
    	int i;
    	for (i=0; i<3; i++)
    	{
    		norm+=vect[i]*vect[i];
    	}
    	norm=sqrt(norm);
    	for (i=0; i<3; i++)
    	{
    		vect[i]/=norm;
    	}
    }

    bool calcExplosion(const Rocket& mv, float x, float y, float z, const float coord[3])
    {
    	float vect1[3], vect2[3];
    	float distance1, distance2, shortest, cos_value=0;
    	int i;
    	vect1[0]=coord[0]-mv.firePos.x;
    	vect1[1]=coord[1]-mv.firePos.y;
    	vect1[2]=coord[2]-mv.firePos.z;

    	distance1=sqrt(vect1[0]*vect1[0]+vect1[1]*vect1[1]+vect1[2]*vect1[2]);

    	vect2[0]=x-mv.firePos.x;
    	vect2[1]=y-mv.firePos.y;
    	vect2[2]=z-mv.firePos.z;
    	distance2=sqrt(vect2[0]*vect2[0]+vect2[1]*vect2[1]+vect2[2]*vect2[2]);

    	for (i=0; i<3; i++)
    	{
    		vect1[i]/=distance1;
    		vect2[i]/=distance2;
    	}

    	for (i=0; i<3; i++)
    	{
    		cos_value+=vect1[i]*vect2[i];
    	}

    	shortest=sqrt(1-cos_value*cos_value)*distance1;
    	//there is two conditions:
    	//1. the rocket must hit the target at some time
    	//2. the hitting time is now
    	if (shortest<ExplosionDistance&&distance2>=distance1*cos_value)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }

    bool calcRocket(int index, DWORD now, float &x, float &y, float &z)
    {
    	float distance;
    	if (now-rockets[index].fireMoment>MaxRocketFlyTime)
    	{
    		if (rocketCount>0)
    		{
    			rocketCount--;
    			//shift array
    			for (int i=index; i<rocketCount; i++)
    			{
    				rockets[index]=rockets[index+1];
    			}
    		}
    		return false; //must not exist anymore
    	}
    	//else

    	distance=(now-rockets[index].fireMoment)*RocketFlySpeed;
    	calcCoord(distance, rockets[index].firePos, x, y, z);
    	for (int i=0; i<targetCount; i++)
    	{
    		if (calcExplosion(rockets[index], x,y,z, targetCoordLists[targets[i].coordIndex]))
    		{
    			explosionIndex=i;
    			return false;
    		}
    	}
    	/*
    	x=distance*cos(deg2rad(rockets[index].firePos.pitch))*
    		sin(deg2rad(rockets[index].firePos.yaw));
    	y=distance*sin(deg2rad(rockets[index].firePos.pitch))*
    		cos(deg2rad(rockets[index].firePos.roll));

    	z=distance*cos(deg2rad(rockets[index].firePos.pitch))*
    		cos(deg2rad(rockets[index].firePos.yaw));
    		*/
    	return true;
    }

    /////////////////////////////////////////////////////////////////////////

    //Variables
    GLdouble P1[4] = {0.0, 1.0, 0.0, 0.0}; //clip y=0-plane
    /*******************************************************************************************/
    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.

    	gluQuadricNormals(pObj,GLU_SMOOTH);
    							// specify one normal per vertex.
    	gluQuadricOrientation(pObj, GLU_OUTSIDE);
    							// For the quadrics object pObj,orientation is either GLU_OUTSIDE (the default)
    							// or GLU_INSIDE, which controls the direction in which normals are pointing.
    	gluQuadricTexture(pObj, GLU_TRUE);
    							// This turns on texture coordinates for our Quadric.
    	gluQuadricDrawStyle(pObj, GLU_FILL);

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

    }

    /****************************************************************************************/
    void drawCylinder(float fbaseRadius, float ftopRadius, float fHeight, GLint slices, GLint rings)
    							// Used to generate a cylinder shape.
    {
    	GLUquadricObj* pObj;
    	pObj =  gluNewQuadric();
    							// Creates a new quadrics object and returns a pointer to it.

    	gluQuadricNormals(pObj,GLU_SMOOTH);
    							// specify one normal per vertex.
    	gluQuadricOrientation(pObj, GLU_OUTSIDE);
    							// For the quadrics object pObj,orientation is either GLU_OUTSIDE (the default)
    							// or GLU_INSIDE, which controls the direction in which normals are pointing.
    	gluQuadricTexture(pObj, GLU_TRUE);
    							// This turns on texture coordinates for our Quadric.
    	gluQuadricDrawStyle(pObj, GLU_FILL);

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

    }
    /****************************************************************************************/
    void drawTreeAt(GLfloat x, GLfloat z)
    {
    	glPushAttrib(GL_COLOR_BUFFER_BIT);

    	glColor3f(0.3,0.2,0.2);
    	glPushMatrix();
    	glTranslatef(x,FLOOR_LEVEL,z);
    	glRotatef(-90.0,1.0,0.0,0.0);
    	drawCylinder(0.15,0.15,0.75,10.0,10.0);
    	glPopMatrix();

    	glColor3f(0.0,0.4,0.0);
    	glPushMatrix();
    	glTranslatef(x,FLOOR_LEVEL,z);
    	glTranslatef(0.0,0.5,0.0);
    	glRotatef(-90.0,1.0,0.0,0.0);
        drawCylinder(0.5,0.0,0.75,10.0,10.0);
    	glPopMatrix();

    	glPushMatrix();
    	glTranslatef(x,FLOOR_LEVEL,z);
    	glTranslatef(0.0,1.0,0.0);
    	glRotatef(-90.0,1.0,0.0,0.0);
        drawCylinder(0.5,0.0,0.75,10.0,10.0);
    	glPopMatrix();

    	glPushMatrix();
    	glTranslatef(x,FLOOR_LEVEL,z);
    	glTranslatef(0.0,1.5,0.0);
    	glRotatef(-90.0,1.0,0.0,0.0);
        drawCylinder(0.5,0.0,0.75,10.0,10.0);
    	glPopMatrix();

    }
    /****************************************************************************************/

    void drawGroundTrees() // dash on the ground to mark the way
    {
    	int i,j;
    	for (i =-50; i< 50 ;)
    	{
    		for(j=-50; j<50;)
    		{
    		  drawTreeAt((i+2),(j+2));

    		  j+=20;
            }

    		i += 20;
    	}
    }
    /****************************************************************************************/
    void drawParaBombAt (GLfloat x, GLfloat z)
    {
    	glPushAttrib(GL_COLOR_BUFFER_BIT);

    	//draw parachute
    	glEnable (GL_CLIP_PLANE3);
    	glColor3f(0.5,0.75,0.5);
    	glPushMatrix();
    	glTranslatef(x,0.0,z);
    	glClipPlane (GL_CLIP_PLANE3, P1); //clip plane at y=0, remove down side
    	drawSphere(0.5,25.0,25.0);
    	glPopMatrix();
    	glDisable (GL_CLIP_PLANE3);

    	//draw ropes
    	glColor3f(0.0,0.0,0.0);

        glBegin(GL_LINES);
    	   glVertex3f(x + 0.5,0.0,z);
    	   glVertex3f(x,-0.5,z);
        glEnd();

    	glBegin(GL_LINES);
    	   glVertex3f(x - 0.5,0.0,z);
    	   glVertex3f(x,-0.5,z);
        glEnd();

    	glBegin(GL_LINES);
    	   glVertex3f(x,0.0,z+0.5);
    	   glVertex3f(x,-0.5,z);
        glEnd();

    	glBegin(GL_LINES);
    	   glVertex3f(x,0.0,z-0.5);
    	   glVertex3f(x,-0.5,z);
        glEnd();

    	//draw bomb
        glPushMatrix();
    	 glTranslatef(x,-0.5,z);
    	 drawSphere(0.15,10,10);
        glPopMatrix();

       glPopAttrib();
    }

    void initializeMovement()
    {
    	movement.x=0;
    	//movement.z=0.55-0.15;
    	//movement.z=0.55;
    	movement.z=0.0;
    	//movement.y=HecLevel+0.09+2.0;
    	movement.y=HecLevel;
    	//movement.y=FLOOR_LEVEL;
    	movement.pitch=0;
    	movement.roll=0;
    	movement.yaw=0.0;
    }

    GLfloat deg2rad(GLfloat deg)
    {
    	return deg/360*2*PI;
    }

    GLfloat rad2deg(GLfloat rad)
    {
    	return rad/2.0/PI*360.0;
    }

    ¡¡

    ¡¡

    file name: texture.h

    /***************************************************************************************************************/
    /* HEADER FILE FOR THE TUTORIAL ON TEXTURE MAPPING.
    /*	Dated : May 27th 2004						Author: Ramgopal R,
    /*												Computer Graphics and Visualization Lab. Concordia University
    /*												e-mail: r_rajago@cs.concordia.ca
    /*												URL: http://www.cs.concordia.ca/~grad/r_rajago/graphics
    /*
    /*Notes and Acknowledgements:					Thanks to DigiBen from GameTutorials.com for a significant
    /*												portion of the write up.
    /**************************************************************************************************************/

    #ifndef __TEXTURE_H
    #define __TEXTURE_H

    #include <GL\glaux.h>

    // This function creates a texture from a BMP file 'strFileName'. It further stores the OpenGL handle to the generated texture
    // in the 'textureID' location of 'textureArray'.
    bool CreateTexture(char* strFileName, GLuint* textureArray, GLint textureID)
    {
    	AUX_RGBImageRec *pBitmap = NULL;					// The glaux data structure to store bitmap.

    	if(!strFileName)									// Return from the function if no file name was passed in
    		return false;

    	// We need to load the texture data, so we use an API that the glaux.lib.

    	pBitmap = auxDIBImageLoad(strFileName);				// Load the bitmap and store the data

    	if(pBitmap == NULL)									// If we can't load the file, quit!
    		return false;

    	// Now that we have the texture data, we need to register our texture with OpenGL
    	// To do this we need to call glGenTextures().  The 1 for the first parameter is
    	// how many texture we want to register this time (we could do a bunch in a row).
    	// The second parameter is the array index that will hold the reference to this texture.

    	// Generate a texture with the associative texture ID stored in the array
    	glGenTextures(1, &textureArray[textureID]);

    	// Now that we have a reference for the texture, we need to bind the texture
    	// to tell OpenGL this is the reference that we are assigning the bitmap data too.
    	// The first parameter tells OpenGL we want are using a 2D texture, while the
    	// second parameter passes in the reference we are going to assign the texture too.
    	// We will use this function later to tell OpenGL we want to use this texture to texture map.

    	// Bind the texture to the texture arrays index and init the texture
    	glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);

    	// Now comes the important part, we actually pass in all the data from the bitmap to
    	// create the texture. Here is what the parameters mean in gluBuild2DMipmaps():
    	// (We want a 2D texture, 3 channels (RGB), bitmap width, bitmap height, It's an RGB format,
    	//  the data is stored as unsigned bytes, and the actuall pixel data);

    	// What is a Mip map?  Mip maps are a bunch of scaled pictures from the original.  This makes
    	// it look better when we are near and farther away from the texture map.  It chooses the
    	// best looking scaled size depending on where the camera is according to the texture map.
    	// Otherwise, if we didn't use mip maps, it would scale the original UP and down which would
    	// look not so good when we got far away or up close, it would look pixelated.

    	// Build Mipmaps (builds different versions of the picture for distances - looks better)
    	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap->sizeX, pBitmap->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pBitmap->data);

    	// Lastly, we need to tell OpenGL the quality of our texture map.  GL_LINEAR_MIPMAP_LINEAR
    	// is the smoothest.  GL_LINEAR_MIPMAP_NEAREST is faster than GL_LINEAR_MIPMAP_LINEAR,
    	// but looks blochy and pixilated.  Good for slower computers though.  Read more about
    	// the MIN and MAG filters in the texture.cpp.

    	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
    	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);

    	// Now we need to free the bitmap data that we loaded since openGL stored it as a texture

    	if (pBitmap)										// If we loaded the bitmap
    	{
    		if (pBitmap->data)								// If there is texture data
    		{
    			free(pBitmap->data);						// Free the texture data, we don't need it anymore
    		}

    		free(pBitmap);									// Free the bitmap structure
    	}
    	return true;
    }

    //NeHeGL
    /*********/
    typedef struct													// Create A Structure for TGA
    {
    	GLubyte	*imageData;											// Image Data (Up To 32 Bits)
    	GLuint	bpp;												// Image Color Depth In Bits Per Pixel.
    	GLuint	width;												// Image Width
    	GLuint	height;												// Image Height
    	GLuint	texID;												// Texture ID Used To Select A Texture
    } TextureImage;

    bool LoadTGA(TextureImage *texture, char *filename)				// Loads A TGA File Into Memory
    {
    	GLubyte		TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};		// Uncompressed TGA Header
    	GLubyte		TGAcompare[12];									// Used To Compare TGA Header
    	GLubyte		header[6];										// First 6 Useful Bytes From The Header
    	GLuint		bytesPerPixel;									// Holds Number Of Bytes Per Pixel Used In The TGA File
    	GLuint		imageSize;										// Used To Store The Image Size When Setting Aside Ram
    	GLuint		temp;											// Temporary Variable
    	GLuint		type=GL_RGBA;									// Set The Default GL Mode To RBGA (32 BPP)

    	FILE *file = fopen(filename, "rb");							// Open The TGA File

    	if(	file==NULL ||											// Does File Even Exist?
    		fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) ||	// Are There 12 Bytes To Read?
    		memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0				||	// Does The Header Match What We Want?
    		fread(header,1,sizeof(header),file)!=sizeof(header))				// If So Read Next 6 Header Bytes
    	{
    		if (file == NULL)										// Did The File Even Exist? *Added Jim Strong*
    			return FALSE;										// Return False
    		else													// Otherwise
    		{
    			fclose(file);										// If Anything Failed, Close The File
    			return FALSE;										// Return False
    		}
    	}

    	texture->width  = header[1] * 256 + header[0];				// Determine The TGA Width	(highbyte*256+lowbyte)
    	texture->height = header[3] * 256 + header[2];				// Determine The TGA Height	(highbyte*256+lowbyte)

     	if(	texture->width	<=0	||									// Is The Width Less Than Or Equal To Zero
    		texture->height	<=0	||									// Is The Height Less Than Or Equal To Zero
    		(header[4]!=24 && header[4]!=32))						// Is The TGA 24 or 32 Bit?
    	{
    		fclose(file);											// If Anything Failed, Close The File
    		return FALSE;											// Return False
    	}

    	texture->bpp	= header[4];								// Grab The TGA's Bits Per Pixel (24 or 32)
    	bytesPerPixel	= texture->bpp/8;							// Divide By 8 To Get The Bytes Per Pixel
    	imageSize		= texture->width*texture->height*bytesPerPixel;	// Calculate The Memory Required For The TGA Data

    	texture->imageData=(GLubyte *)malloc(imageSize);			// Reserve Memory To Hold The TGA Data

    	if(	texture->imageData==NULL ||								// Does The Storage Memory Exist?
    		fread(texture->imageData, 1, imageSize, file)!=imageSize)	// Does The Image Size Match The Memory Reserved?
    	{
    		if(texture->imageData!=NULL)							// Was Image Data Loaded
    			free(texture->imageData);							// If So, Release The Image Data

    		fclose(file);											// Close The File
    		return FALSE;											// Return False
    	}

    	for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel)			// Loop Through The Image Data
    	{															// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
    		temp=texture->imageData[i];								// Temporarily Store The Value At Image Data 'i'
    		texture->imageData[i] = texture->imageData[i + 2];		// Set The 1st Byte To The Value Of The 3rd Byte
    		texture->imageData[i + 2] = temp;						// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
    	}

    	fclose (file);												// Close The File

    	// Build A Texture From The Data
    	glGenTextures(1, &texture[0].texID);						// Generate OpenGL texture IDs

    	glBindTexture(GL_TEXTURE_2D, texture[0].texID);				// Bind Our Texture
    	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// Linear Filtered
    	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	// Linear Filtered

    	if (texture[0].bpp==24)										// Was The TGA 24 Bits
    	{
    		type=GL_RGB;											// If So Set The 'type' To GL_RGB
    	}

    	glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);

    	return true;												// Texture Building Went Ok, Return True
    }
    #endif //	__TEXTURE_H definition ends here

    ¡¡

    file name: glm.h

    /*
          glm.h
          Nate Robins, 1997, 2000
          nate@pobox.com, http://www.pobox.com/~nate

          Wavefront OBJ model file format reader/writer/manipulator.

          Includes routines for generating smooth normals with
          preservation of edges, welding redundant vertices & texture
          coordinate generation (spheremap and planar projections) + more.

    	  Modified by Ramgopal R to accomodate point rendering.

     */

    #include <GL/glut.h>

    #ifndef M_PI
    #define M_PI 3.14159265f
    #endif

    #define GLM_POINTS   (1 << 0)       /* render with only vertices */
    #define GLM_FLAT     (1 << 1)       /* render with facet normals */
    #define GLM_SMOOTH   (1 << 2)       /* render with vertex normals */
    #define GLM_TEXTURE  (1 << 3)       /* render with texture coords */
    #define GLM_COLOR    (1 << 4)       /* render with colors */
    #define GLM_MATERIAL (1 << 5)       /* render with materials */

    /* GLMmaterial: Structure that defines a material in a model.
     */
    typedef struct _GLMmaterial
    {
      char* name;                   /* name of material */
      GLfloat diffuse[4];           /* diffuse component */
      GLfloat ambient[4];           /* ambient component */
      GLfloat specular[4];          /* specular component */
      GLfloat emmissive[4];         /* emmissive component */
      GLfloat shininess;            /* specular exponent */
    } GLMmaterial;

    /* GLMtriangle: Structure that defines a triangle in a model.
     */
    typedef struct _GLMtriangle {
      GLuint vindices[3];           /* array of triangle vertex indices */
      GLuint nindices[3];           /* array of triangle normal indices */
      GLuint tindices[3];           /* array of triangle texcoord indices*/
      GLuint findex;                /* index of triangle facet normal */
    } GLMtriangle;

    /* GLMgroup: Structure that defines a group in a model.
     */
    typedef struct _GLMgroup {
      char*             name;           /* name of this group */
      GLuint            numtriangles;   /* number of triangles in this group */
      GLuint*           triangles;      /* array of triangle indices */
      GLuint            material;       /* index to material for group */
      struct _GLMgroup* next;           /* pointer to next group in model */
    } GLMgroup;

    /* GLMmodel: Structure that defines a model.
     */
    typedef struct _GLMmodel {
      char*    pathname;            /* path to this model */
      char*    mtllibname;          /* name of the material library */

      GLuint   numvertices;         /* number of vertices in model */
      GLfloat* vertices;            /* array of vertices  */

      GLuint   numnormals;          /* number of normals in model */
      GLfloat* normals;             /* array of normals */

      GLuint   numtexcoords;        /* number of texcoords in model */
      GLfloat* texcoords;           /* array of texture coordinates */

      GLuint   numfacetnorms;       /* number of facetnorms in model */
      GLfloat* facetnorms;          /* array of facetnorms */

      GLuint       numtriangles;    /* number of triangles in model */
      GLMtriangle* triangles;       /* array of triangles */

      GLuint       nummaterials;    /* number of materials in model */
      GLMmaterial* materials;       /* array of materials */

      GLuint       numgroups;       /* number of groups in model */
      GLMgroup*    groups;          /* linked list of groups */

      GLfloat position[3];          /* position of the model */

    } GLMmodel;

    /* glmUnitize: "unitize" a model by translating it to the origin and
     * scaling it to fit in a unit cube around the origin.  Returns the
     * scalefactor used.
     *
     * model - properly initialized GLMmodel structure
     */
    GLfloat
    glmUnitize(GLMmodel* model);

    /* glmDimensions: Calculates the dimensions (width, height, depth) of
     * a model.
     *
     * model      - initialized GLMmodel structure
     * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
     */
    GLvoid
    glmDimensions(GLMmodel* model, GLfloat* dimensions);

    /* glmScale: Scales a model by a given amount.
     *
     * model - properly initialized GLMmodel structure
     * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
     */
    GLvoid
    glmScale(GLMmodel* model, GLfloat scale);

    /* glmReverseWinding: Reverse the polygon winding for all polygons in
     * this model.  Default winding is counter-clockwise.  Also changes
     * the direction of the normals.
     *
     * model - properly initialized GLMmodel structure
     */
    GLvoid
    glmReverseWinding(GLMmodel* model);

    /* glmFacetNormals: Generates facet normals for a model (by taking the
     * cross product of the two vectors derived from the sides of each
     * triangle).  Assumes a counter-clockwise winding.
     *
     * model - initialized GLMmodel structure
     */
    GLvoid
    glmFacetNormals(GLMmodel* model);

    /* glmVertexNormals: Generates smooth vertex normals for a model.
     * First builds a list of all the triangles each vertex is in.  Then
     * loops through each vertex in the the list averaging all the facet
     * normals of the triangles each vertex is in.  Finally, sets the
     * normal index in the triangle for the vertex to the generated smooth
     * normal.  If the dot product of a facet normal and the facet normal
     * associated with the first triangle in the list of triangles the
     * current vertex is in is greater than the cosine of the angle
     * parameter to the function, that facet normal is not added into the
     * average normal calculation and the corresponding vertex is given
     * the facet normal.  This tends to preserve hard edges.  The angle to
     * use depends on the model, but 90 degrees is usually a good start.
     *
     * model - initialized GLMmodel structure
     * angle - maximum angle (in degrees) to smooth across
     */
    GLvoid
    glmVertexNormals(GLMmodel* model, GLfloat angle);

    /* glmLinearTexture: Generates texture coordinates according to a
     * linear projection of the texture map.  It generates these by
     * linearly mapping the vertices onto a square.
     *
     * model - pointer to initialized GLMmodel structure
     */
    GLvoid
    glmLinearTexture(GLMmodel* model);

    /* glmSpheremapTexture: Generates texture coordinates according to a
     * spherical projection of the texture map.  Sometimes referred to as
     * spheremap, or reflection map texture coordinates.  It generates
     * these by using the normal to calculate where that vertex would map
     * onto a sphere.  Since it is impossible to map something flat
     * perfectly onto something spherical, there is distortion at the
     * poles.  This particular implementation causes the poles along the X
     * axis to be distorted.
     *
     * model - pointer to initialized GLMmodel structure
     */
    GLvoid
    glmSpheremapTexture(GLMmodel* model);

    /* glmDelete: Deletes a GLMmodel structure.
     *
     * model - initialized GLMmodel structure
     */
    GLvoid
    glmDelete(GLMmodel* model);

    /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
     * Returns a pointer to the created object which should be free'd with
     * glmDelete().
     *
     * filename - name of the file containing the Wavefront .OBJ format data.
     */
    GLMmodel*
    glmReadOBJ(char* filename);

    /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
     * a file.
     *
     * model    - initialized GLMmodel structure
     * filename - name of the file to write the Wavefront .OBJ format data to
     * mode     - a bitwise or of values describing what is written to the file
     *            GLM_NONE    -  write only vertices
     *            GLM_FLAT    -  write facet normals
     *            GLM_SMOOTH  -  write vertex normals
     *            GLM_TEXTURE -  write texture coords
     *            GLM_FLAT and GLM_SMOOTH should not both be specified.
     */
    GLvoid
    glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode);

    /* glmDraw: Renders the model to the current OpenGL context using the
     * mode specified.
     *
     * model    - initialized GLMmodel structure
     * mode     - a bitwise OR of values describing what is to be rendered.
     *            GLM_NONE    -  render with only vertices
     *            GLM_FLAT    -  render with facet normals
     *            GLM_SMOOTH  -  render with vertex normals
     *            GLM_TEXTURE -  render with texture coords
     *            GLM_FLAT and GLM_SMOOTH should not both be specified.
     */
    GLvoid
    glmDraw(GLMmodel* model, GLuint mode);

    /* glmList: Generates and returns a display list for the model using
     * the mode specified.
     *
     * model    - initialized GLMmodel structure
     * mode     - a bitwise OR of values describing what is to be rendered.
     *            GLM_NONE    -  render with only vertices
     *            GLM_FLAT    -  render with facet normals
     *            GLM_SMOOTH  -  render with vertex normals
     *            GLM_TEXTURE -  render with texture coords
     *            GLM_FLAT and GLM_SMOOTH should not both be specified.
     */
    GLuint
    glmList(GLMmodel* model, GLuint mode);

    /* glmWeld: eliminate (weld) vectors that are within an epsilon of
     * each other.
     *
     * model      - initialized GLMmodel structure
     * epsilon    - maximum difference between vertices
     *              ( 0.00001 is a good start for a unitized model)
     *
     */
    GLvoid
    glmWeld(GLMmodel* model, GLfloat epsilon);

    /* glmReadPPM: read a PPM raw (type P6) file.  The PPM file has a header
     * that should look something like:
     *
     *    P6
     *    # comment
     *    width height max_value
     *    rgbrgbrgb...
     *
     * where "P6" is the magic cookie which identifies the file type and
     * should be the only characters on the first line followed by a
     * carriage return.  Any line starting with a # mark will be treated
     * as a comment and discarded.   After the magic cookie, three integer
     * values are expected: width, height of the image and the maximum
     * value for a pixel (max_value must be < 256 for PPM raw files).  The
     * data section consists of width*height rgb triplets (one byte each)
     * in binary format (i.e., such as that written with fwrite() or
     * equivalent).
     *
     * The rgb data is returned as an array of unsigned chars (packed
     * rgb).  The malloc()'d memory should be free()'d by the caller.  If
     * an error occurs, an error message is sent to stderr and NULL is
     * returned.
     *
     * filename   - name of the .ppm file.
     * width      - will contain the width of the image on return.
     * height     - will contain the height of the image on return.
     *
     */
    GLubyte*
    glmReadPPM(char* filename, int* width, int* height);

    ¡¡

    file name: glm.cpp

    /*
          glm.c
          Nate Robins, 1997, 2000
          nate@pobox.com, http://www.pobox.com/~nate

          Wavefront OBJ model file format reader/writer/manipulator.

          Includes routines for generating smooth normals with
          preservation of edges, welding redundant vertices & texture
          coordinate generation (spheremap and planar projections) + more.

    */

    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    #include "glm.h"

    #define T(x) (model->triangles[(x)])

    /* _GLMnode: general purpose node */
    typedef struct _GLMnode {
        GLuint         index;
        GLboolean      averaged;
        struct _GLMnode* next;
    } GLMnode;

    /* glmMax: returns the maximum of two floats */
    static GLfloat
    glmMax(GLfloat a, GLfloat b)
    {
        if (b > a)
            return b;
        return a;
    }

    /* glmAbs: returns the absolute value of a float */
    static GLfloat
    glmAbs(GLfloat f)
    {
        if (f < 0)
            return -f;
        return f;
    }

    /* glmDot: compute the dot product of two vectors
     *
     * u - array of 3 GLfloats (GLfloat u[3])
     * v - array of 3 GLfloats (GLfloat v[3])
     */
    static GLfloat
    glmDot(GLfloat* u, GLfloat* v)
    {
        assert(u); assert(v);

        return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
    }

    /* glmCross: compute the cross product of two vectors
     *
     * u - array of 3 GLfloats (GLfloat u[3])
     * v - array of 3 GLfloats (GLfloat v[3])
     * n - array of 3 GLfloats (GLfloat n[3]) to return the cross product in
     */
    static GLvoid
    glmCross(GLfloat* u, GLfloat* v, GLfloat* n)
    {
        assert(u); assert(v); assert(n);

        n[0] = u[1]*v[2] - u[2]*v[1];
        n[1] = u[2]*v[0] - u[0]*v[2];
        n[2] = u[0]*v[1] - u[1]*v[0];
    }

    /* glmNormalize: normalize a vector
     *
     * v - array of 3 GLfloats (GLfloat v[3]) to be normalized
     */
    static GLvoid
    glmNormalize(GLfloat* v)
    {
        GLfloat l;

        assert(v);

        l = (GLfloat)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
        v[0] /= l;
        v[1] /= l;
        v[2] /= l;
    }

    /* glmEqual: compares two vectors and returns GL_TRUE if they are
     * equal (within a certain threshold) or GL_FALSE if not. An epsilon
     * that works fairly well is 0.000001.
     *
     * u - array of 3 GLfloats (GLfloat u[3])
     * v - array of 3 GLfloats (GLfloat v[3])
     */
    static GLboolean
    glmEqual(GLfloat* u, GLfloat* v, GLfloat epsilon)
    {
        if (glmAbs(u[0] - v[0]) < epsilon &&
            glmAbs(u[1] - v[1]) < epsilon &&
            glmAbs(u[2] - v[2]) < epsilon)
        {
            return GL_TRUE;
        }
        return GL_FALSE;
    }

    /* glmWeldVectors: eliminate (weld) vectors that are within an
     * epsilon of each other.
     *
     * vectors     - array of GLfloat[3]'s to be welded
     * numvectors - number of GLfloat[3]'s in vectors
     * epsilon     - maximum difference between vectors
     *
     */
    GLfloat*
    glmWeldVectors(GLfloat* vectors, GLuint* numvectors, GLfloat epsilon)
    {
        GLfloat* copies;
        GLuint   copied;
        GLuint   i, j;

        copies = (GLfloat*)malloc(sizeof(GLfloat) * 3 * (*numvectors + 1));
        memcpy(copies, vectors, (sizeof(GLfloat) * 3 * (*numvectors + 1)));

        copied = 1;
        for (i = 1; i <= *numvectors; i++) {
            for (j = 1; j <= copied; j++) {
                if (glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) {
                    goto duplicate;
                }
            }

            /* must not be any duplicates -- add to the copies array */
            copies[3 * copied + 0] = vectors[3 * i + 0];
            copies[3 * copied + 1] = vectors[3 * i + 1];
            copies[3 * copied + 2] = vectors[3 * i + 2];
            j = copied;             /* pass this along for below */
            copied++;

    duplicate:
    /* set the first component of this vector to point at the correct
            index into the new copies array */
            vectors[3 * i + 0] = (GLfloat)j;
        }

        *numvectors = copied-1;
        return copies;
    }

    /* glmFindGroup: Find a group in the model */
    GLMgroup*
    glmFindGroup(GLMmodel* model, char* name)
    {
        GLMgroup* group;

        assert(model);

        group = model->groups;
        while(group) {
            if (!strcmp(name, group->name))
                break;
            group = group->next;
        }

        return group;
    }

    /* glmAddGroup: Add a group to the model */
    GLMgroup*
    glmAddGroup(GLMmodel* model, char* name)
    {
        GLMgroup* group;

        group = glmFindGroup(model, name);
        if (!group) {
            group = (GLMgroup*)malloc(sizeof(GLMgroup));
            group->name = strdup(name);
            group->material = 0;
            group->numtriangles = 0;
            group->triangles = NULL;
            group->next = model->groups;
            model->groups = group;
            model->numgroups++;
        }

        return group;
    }

    /* glmFindGroup: Find a material in the model */
    GLuint
    glmFindMaterial(GLMmodel* model, char* name)
    {
        GLuint i;

        /* This search is not efficient enough*/
        for (i = 0; i < model->nummaterials; i++) {
            if (!strcmp(model->materials[i].name, name))
                goto found;
        }

        /* didn't find the name, so print a warning and return the default
        material (0). */
        printf("glmFindMaterial():  can't find material \"%s\".\n", name);
        i = 0;

    found:
        return i;
    }

    /* glmDirName: return the directory given a path
     *
     * path - filesystem path
     *
     * NOTE: the return value should be free'd.
     */
    static char*
    glmDirName(char* path)
    {
        char* dir;
        char* s;

        dir = strdup(path);

        s = strrchr(dir, '/');
        if (s)
            s[1] = '\0';
        else
            dir[0] = '\0';

        return dir;
    }

    /* glmReadMTL: read a wavefront material library file
     *
     * model - properly initialized GLMmodel structure
     * name  - name of the material library
     */
    static GLvoid
    glmReadMTL(GLMmodel* model, char* name)
    {
        FILE* file;
        char* dir;
        char* filename;
        char    buf[128];
        GLuint nummaterials, i;

        dir = glmDirName(model->pathname);
        filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1));
        strcpy(filename, dir);
        strcat(filename, name);
        free(dir);

        file = fopen(filename, "r");
        if (!file) {
            fprintf(stderr, "glmReadMTL() failed: can't open material file \"%s\".\n",
                filename);
            exit(1);
        }
        free(filename);

        /* count the number of materials in the file */
        nummaterials = 1;
        while(fscanf(file, "%s", buf) != EOF) {
            switch(buf[0]) {
            case '#':               /* comment */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            case 'n':               /* newmtl */
                fgets(buf, sizeof(buf), file);
                nummaterials++;
                sscanf(buf, "%s %s", buf, buf);
                break;
            default:
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            }
        }

        rewind(file);

        model->materials = (GLMmaterial*)malloc(sizeof(GLMmaterial) * nummaterials);
        model->nummaterials = nummaterials;

        /* set the default material */
        for (i = 0; i < nummaterials; i++) {
            model->materials[i].name = NULL;
            model->materials[i].shininess = 65.0;
            model->materials[i].diffuse[0] = 0.8;
            model->materials[i].diffuse[1] = 0.8;
            model->materials[i].diffuse[2] = 0.8;
            model->materials[i].diffuse[3] = 1.0;
            model->materials[i].ambient[0] = 0.2;
            model->materials[i].ambient[1] = 0.2;
            model->materials[i].ambient[2] = 0.2;
            model->materials[i].ambient[3] = 1.0;
            model->materials[i].specular[0] = 0.0;
            model->materials[i].specular[1] = 0.0;
            model->materials[i].specular[2] = 0.0;
            model->materials[i].specular[3] = 1.0;
        }
        model->materials[0].name = strdup("default");

        /* now, read in the data */
        nummaterials = 0;
        while(fscanf(file, "%s", buf) != EOF) {
            switch(buf[0]) {
            case '#':               /* comment */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            case 'n':               /* newmtl */
                fgets(buf, sizeof(buf), file);
                sscanf(buf, "%s %s", buf, buf);
                nummaterials++;
                model->materials[nummaterials].name = strdup(buf);
                break;
            case 'N':
                fscanf(file, "%f", &model->materials[nummaterials].shininess);
                /* wavefront shininess is from [0, 1000], so scale for OpenGL */
                model->materials[nummaterials].shininess /= 1000.0;
                model->materials[nummaterials].shininess *= 128.0;
                break;
            case 'K':
                switch(buf[1]) {
                case 'd':
                    fscanf(file, "%f %f %f",
                        &model->materials[nummaterials].diffuse[0],
                        &model->materials[nummaterials].diffuse[1],
                        &model->materials[nummaterials].diffuse[2]);
                    break;
                case 's':
                    fscanf(file, "%f %f %f",
                        &model->materials[nummaterials].specular[0],
                        &model->materials[nummaterials].specular[1],
                        &model->materials[nummaterials].specular[2]);
                    break;
                case 'a':
                    fscanf(file, "%f %f %f",
                        &model->materials[nummaterials].ambient[0],
                        &model->materials[nummaterials].ambient[1],
                        &model->materials[nummaterials].ambient[2]);
                    break;
                default:
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    break;
                }
                break;
                default:
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    break;
            }
        }

        fclose(file);
    }

    /* glmWriteMTL: write a wavefront material library file
     *
     * model   - properly initialized GLMmodel structure
     * modelpath  - pathname of the model being written
     * mtllibname - name of the material library to be written
     */
    static GLvoid
    glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
    {
        FILE* file;
        char* dir;
        char* filename;
        GLMmaterial* material;
        GLuint i;

        dir = glmDirName(modelpath);
        filename = (char*)malloc(sizeof(char) * (strlen(dir)+strlen(mtllibname)));
        strcpy(filename, dir);
        strcat(filename, mtllibname);
        free(dir);

        /* open the file */
        file = fopen(filename, "w");
        if (!file) {
            fprintf(stderr, "glmWriteMTL() failed: can't open file \"%s\".\n",
                filename);
            exit(1);
        }
        free(filename);

        /* spit out a header */
        fprintf(file, "#  \n");
        fprintf(file, "#  Wavefront MTL generated by GLM library\n");
        fprintf(file, "#  \n");
        fprintf(file, "#  GLM library\n");

        for (i = 0; i < model->nummaterials; i++) {
            material = &model->materials[i];
            fprintf(file, "newmtl %s\n", material->name);
            fprintf(file, "Ka %f %f %f\n",
                material->ambient[0], material->ambient[1], material->ambient[2]);
            fprintf(file, "Kd %f %f %f\n",
                material->diffuse[0], material->diffuse[1], material->diffuse[2]);
            fprintf(file, "Ks %f %f %f\n",
                material->specular[0],material->specular[1],material->specular[2]);
            fprintf(file, "Ns %f\n", material->shininess / 128.0 * 1000.0);
            fprintf(file, "\n");
        }
    }

    /* glmFirstPass: first pass at a Wavefront OBJ file that gets all the
     * statistics of the model (such as #vertices, #normals, etc)
     *
     * model - properly initialized GLMmodel structure
     * file  - (fopen'd) file descriptor
     */
    static GLvoid
    glmFirstPass(GLMmodel* model, FILE* file)
    {
        GLuint  numvertices;        /* number of vertices in model */
        GLuint  numnormals;         /* number of normals in model */
        GLuint  numtexcoords;       /* number of texcoords in model */
        GLuint  numtriangles;       /* number of triangles in model */
        GLMgroup* group;            /* current group */
        unsigned    v, n, t;
        char        buf[128];

        /* make a default group */
        group = glmAddGroup(model, "default");

        numvertices = numnormals = numtexcoords = numtriangles = 0;
        while(fscanf(file, "%s", buf) != EOF) {
            switch(buf[0]) {
            case '#':               /* comment */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            case 'v':               /* v, vn, vt */
                switch(buf[1]) {
                case '\0':          /* vertex */
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    numvertices++;
                    break;
                case 'n':           /* normal */
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    numnormals++;
                    break;
                case 't':           /* texcoord */
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    numtexcoords++;
                    break;
                default:
                    printf("glmFirstPass(): Unknown token \"%s\".\n", buf);
                    exit(1);
                    break;
                }
                break;
                case 'm':
                    fgets(buf, sizeof(buf), file);
                    sscanf(buf, "%s %s", buf, buf);
                    model->mtllibname = strdup(buf);
                    glmReadMTL(model, buf);
                    break;
                case 'u':
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    break;
                case 'g':               /* group */
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
    #if SINGLE_STRING_GROUP_NAMES
                    sscanf(buf, "%s", buf);
    #else
                    buf[strlen(buf)-1] = '\0';  /* nuke '\n' */
    #endif
                    group = glmAddGroup(model, buf);
                    break;
                case 'f':               /* face */
                    v = n = t = 0;
                    fscanf(file, "%s", buf);
                    /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
                    if (strstr(buf, "//")) {
                        /* v//n */
                        sscanf(buf, "%d//%d", &v, &n);
                        fscanf(file, "%d//%d", &v, &n);
                        fscanf(file, "%d//%d", &v, &n);
                        numtriangles++;
                        group->numtriangles++;
                        while(fscanf(file, "%d//%d", &v, &n) > 0) {
                            numtriangles++;
                            group->numtriangles++;
                        }
                    } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
                        /* v/t/n */
                        fscanf(file, "%d/%d/%d", &v, &t, &n);
                        fscanf(file, "%d/%d/%d", &v, &t, &n);
                        numtriangles++;
                        group->numtriangles++;
                        while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
                            numtriangles++;
                            group->numtriangles++;
                        }
                    } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
                        /* v/t */
                        fscanf(file, "%d/%d", &v, &t);
                        fscanf(file, "%d/%d", &v, &t);
                        numtriangles++;
                        group->numtriangles++;
                        while(fscanf(file, "%d/%d", &v, &t) > 0) {
                            numtriangles++;
                            group->numtriangles++;
                        }
                    } else {
                        /* v */
                        fscanf(file, "%d", &v);
                        fscanf(file, "%d", &v);
                        numtriangles++;
                        group->numtriangles++;
                        while(fscanf(file, "%d", &v) > 0) {
                            numtriangles++;
                            group->numtriangles++;
                        }
                    }
                    break;

                default:
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    break;
            }
      }

      /* set the stats in the model structure */
      model->numvertices  = numvertices;
      model->numnormals   = numnormals;
      model->numtexcoords = numtexcoords;
      model->numtriangles = numtriangles;

      /* allocate memory for the triangles in each group */
      group = model->groups;
      while(group) {
          group->triangles = (GLuint*)malloc(sizeof(GLuint) * group->numtriangles);
          group->numtriangles = 0;
          group = group->next;
      }
    }

    /* glmSecondPass: second pass at a Wavefront OBJ file that gets all
     * the data.
     *
     * model - properly initialized GLMmodel structure
     * file  - (fopen'd) file descriptor
     */
    static GLvoid
    glmSecondPass(GLMmodel* model, FILE* file)
    {
        GLuint  numvertices;        /* number of vertices in model */
        GLuint  numnormals;         /* number of normals in model */
        GLuint  numtexcoords;       /* number of texcoords in model */
        GLuint  numtriangles;       /* number of triangles in model */
        GLfloat*    vertices;           /* array of vertices  */
        GLfloat*    normals;            /* array of normals */
        GLfloat*    texcoords;          /* array of texture coordinates */
        GLMgroup* group;            /* current group pointer */
        GLuint  material;           /* current material */
        GLuint  v, n, t;
        char        buf[128];

        /* set the pointer shortcuts */
        vertices       = model->vertices;
        normals    = model->normals;
        texcoords    = model->texcoords;
        group      = model->groups;

        /* on the second pass through the file, read all the data into the
        allocated arrays */
        numvertices = numnormals = numtexcoords = 1;
        numtriangles = 0;
        material = 0;
        while(fscanf(file, "%s", buf) != EOF) {
            switch(buf[0]) {
            case '#':               /* comment */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            case 'v':               /* v, vn, vt */
                switch(buf[1]) {
                case '\0':          /* vertex */
                    fscanf(file, "%f %f %f",
                        &vertices[3 * numvertices + 0],
                        &vertices[3 * numvertices + 1],
                        &vertices[3 * numvertices + 2]);
                    numvertices++;
                    break;
                case 'n':           /* normal */
                    fscanf(file, "%f %f %f",
                        &normals[3 * numnormals + 0],
                        &normals[3 * numnormals + 1],
                        &normals[3 * numnormals + 2]);
                    numnormals++;
                    break;
                case 't':           /* texcoord */
                    fscanf(file, "%f %f",
                        &texcoords[2 * numtexcoords + 0],
                        &texcoords[2 * numtexcoords + 1]);
                    numtexcoords++;
                    break;
                }
                break;
                case 'u':
                    fgets(buf, sizeof(buf), file);
                    sscanf(buf, "%s %s", buf, buf);
                    group->material = material = glmFindMaterial(model, buf);
                    break;
                case 'g':               /* group */
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
    #if SINGLE_STRING_GROUP_NAMES
                    sscanf(buf, "%s", buf);
    #else
                    buf[strlen(buf)-1] = '\0';  /* nuke '\n' */
    #endif
                    group = glmFindGroup(model, buf);
                    group->material = material;
                    break;
                case 'f':               /* face */
                    v = n = t = 0;
                    fscanf(file, "%s", buf);
                    /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
                    if (strstr(buf, "//")) {
                        /* v//n */
                        sscanf(buf, "%d//%d", &v, &n);
                        T(numtriangles).vindices[0] = v;
                        T(numtriangles).nindices[0] = n;
                        fscanf(file, "%d//%d", &v, &n);
                        T(numtriangles).vindices[1] = v;
                        T(numtriangles).nindices[1] = n;
                        fscanf(file, "%d//%d", &v, &n);
                        T(numtriangles).vindices[2] = v;
                        T(numtriangles).nindices[2] = n;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                        while(fscanf(file, "%d//%d", &v, &n) > 0) {
                            T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                            T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
                            T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                            T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
                            T(numtriangles).vindices[2] = v;
                            T(numtriangles).nindices[2] = n;
                            group->triangles[group->numtriangles++] = numtriangles;
                            numtriangles++;
                        }
                    } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
                        /* v/t/n */
                        T(numtriangles).vindices[0] = v;
                        T(numtriangles).tindices[0] = t;
                        T(numtriangles).nindices[0] = n;
                        fscanf(file, "%d/%d/%d", &v, &t, &n);
                        T(numtriangles).vindices[1] = v;
                        T(numtriangles).tindices[1] = t;
                        T(numtriangles).nindices[1] = n;
                        fscanf(file, "%d/%d/%d", &v, &t, &n);
                        T(numtriangles).vindices[2] = v;
                        T(numtriangles).tindices[2] = t;
                        T(numtriangles).nindices[2] = n;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                        while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
                            T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                            T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
                            T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
                            T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                            T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
                            T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
                            T(numtriangles).vindices[2] = v;
                            T(numtriangles).tindices[2] = t;
                            T(numtriangles).nindices[2] = n;
                            group->triangles[group->numtriangles++] = numtriangles;
                            numtriangles++;
                        }
                    } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
                        /* v/t */
                        T(numtriangles).vindices[0] = v;
                        T(numtriangles).tindices[0] = t;
                        fscanf(file, "%d/%d", &v, &t);
                        T(numtriangles).vindices[1] = v;
                        T(numtriangles).tindices[1] = t;
                        fscanf(file, "%d/%d", &v, &t);
                        T(numtriangles).vindices[2] = v;
                        T(numtriangles).tindices[2] = t;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                        while(fscanf(file, "%d/%d", &v, &t) > 0) {
                            T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                            T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
                            T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                            T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
                            T(numtriangles).vindices[2] = v;
                            T(numtriangles).tindices[2] = t;
                            group->triangles[group->numtriangles++] = numtriangles;
                            numtriangles++;
                        }
                    } else {
                        /* v */
                        sscanf(buf, "%d", &v);
                        T(numtriangles).vindices[0] = v;
                        fscanf(file, "%d", &v);
                        T(numtriangles).vindices[1] = v;
                        fscanf(file, "%d", &v);
                        T(numtriangles).vindices[2] = v;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                        while(fscanf(file, "%d", &v) > 0) {
                            T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                            T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                            T(numtriangles).vindices[2] = v;
                            group->triangles[group->numtriangles++] = numtriangles;
                            numtriangles++;
                        }
                    }
                    break;

                default:
                    /* eat up rest of line */
                    fgets(buf, sizeof(buf), file);
                    break;
        }
      }

    #if 0
      /* announce the memory requirements */
      printf(" Memory: %d bytes\n",
          numvertices  * 3*sizeof(GLfloat) +
          numnormals   * 3*sizeof(GLfloat) * (numnormals ? 1 : 0) +
          numtexcoords * 3*sizeof(GLfloat) * (numtexcoords ? 1 : 0) +
          numtriangles * sizeof(GLMtriangle));
    #endif
    }

    /* public functions */

    /* glmUnitize: "unitize" a model by translating it to the origin and
     * scaling it to fit in a unit cube around the origin.   Returns the
     * scalefactor used.
     *
     * model - properly initialized GLMmodel structure
     */
    GLfloat
    glmUnitize(GLMmodel* model)
    {
        GLuint  i;
        GLfloat maxx, minx, maxy, miny, maxz, minz;
        GLfloat cx, cy, cz, w, h, d;
        GLfloat scale;

        assert(model);
        assert(model->vertices);

        /* get the max/mins */
        maxx = minx = model->vertices[3 + 0];
        maxy = miny = model->vertices[3 + 1];
        maxz = minz = model->vertices[3 + 2];
        for (i = 1; i <= model->numvertices; i++) {
            if (maxx < model->vertices[3 * i + 0])
                maxx = model->vertices[3 * i + 0];
            if (minx > model->vertices[3 * i + 0])
                minx = model->vertices[3 * i + 0];

            if (maxy < model->vertices[3 * i + 1])
                maxy = model->vertices[3 * i + 1];
            if (miny > model->vertices[3 * i + 1])
                miny = model->vertices[3 * i + 1];

            if (maxz < model->vertices[3 * i + 2])
                maxz = model->vertices[3 * i + 2];
            if (minz > model->vertices[3 * i + 2])
                minz = model->vertices[3 * i + 2];
        }

        /* calculate model width, height, and depth */
        w = glmAbs(maxx) + glmAbs(minx);
        h = glmAbs(maxy) + glmAbs(miny);
        d = glmAbs(maxz) + glmAbs(minz);

        /* calculate center of the model */
        cx = (maxx + minx) / 2.0;
        cy = (maxy + miny) / 2.0;
        cz = (maxz + minz) / 2.0;

        /* calculate unitizing scale factor */
        scale = 2.0 / glmMax(glmMax(w, h), d);

        /* translate around center then scale */
        for (i = 1; i <= model->numvertices; i++) {
            model->vertices[3 * i + 0] -= cx;
            model->vertices[3 * i + 1] -= cy;
            model->vertices[3 * i + 2] -= cz;
            model->vertices[3 * i + 0] *= scale;
            model->vertices[3 * i + 1] *= scale;
            model->vertices[3 * i + 2] *= scale;
        }

        return scale;
    }

    /* glmDimensions: Calculates the dimensions (width, height, depth) of
     * a model.
     *
     * model   - initialized GLMmodel structure
     * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
     */
    GLvoid
    glmDimensions(GLMmodel* model, GLfloat* dimensions)
    {
        GLuint i;
        GLfloat maxx, minx, maxy, miny, maxz, minz;

        assert(model);
        assert(model->vertices);
        assert(dimensions);

        /* get the max/mins */
        maxx = minx = model->vertices[3 + 0];
        maxy = miny = model->vertices[3 + 1];
        maxz = minz = model->vertices[3 + 2];
        for (i = 1; i <= model->numvertices; i++) {
            if (maxx < model->vertices[3 * i + 0])
                maxx = model->vertices[3 * i + 0];
            if (minx > model->vertices[3 * i + 0])
                minx = model->vertices[3 * i + 0];

            if (maxy < model->vertices[3 * i + 1])
                maxy = model->vertices[3 * i + 1];
            if (miny > model->vertices[3 * i + 1])
                miny = model->vertices[3 * i + 1];

            if (maxz < model->vertices[3 * i + 2])
                maxz = model->vertices[3 * i + 2];
            if (minz > model->vertices[3 * i + 2])
                minz = model->vertices[3 * i + 2];
        }

        /* calculate model width, height, and depth */
        dimensions[0] = glmAbs(maxx) + glmAbs(minx);
        dimensions[1] = glmAbs(maxy) + glmAbs(miny);
        dimensions[2] = glmAbs(maxz) + glmAbs(minz);
    }

    /* glmScale: Scales a model by a given amount.
     *
     * model - properly initialized GLMmodel structure
     * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
     */
    GLvoid
    glmScale(GLMmodel* model, GLfloat scale)
    {
        GLuint i;

        for (i = 1; i <= model->numvertices; i++) {
            model->vertices[3 * i + 0] *= scale;
            model->vertices[3 * i + 1] *= scale;
            model->vertices[3 * i + 2] *= scale;
        }
    }

    /* glmReverseWinding: Reverse the polygon winding for all polygons in
     * this model.   Default winding is counter-clockwise.  Also changes
     * the direction of the normals.
     *
     * model - properly initialized GLMmodel structure
     */
    GLvoid
    glmReverseWinding(GLMmodel* model)
    {
        GLuint i, swap;

        assert(model);

        for (i = 0; i < model->numtriangles; i++) {
            swap = T(i).vindices[0];
            T(i).vindices[0] = T(i).vindices[2];
            T(i).vindices[2] = swap;

            if (model->numnormals) {
                swap = T(i).nindices[0];
                T(i).nindices[0] = T(i).nindices[2];
                T(i).nindices[2] = swap;
            }

            if (model->numtexcoords) {
                swap = T(i).tindices[0];
                T(i).tindices[0] = T(i).tindices[2];
                T(i).tindices[2] = swap;
            }
        }

        /* reverse facet normals */
        for (i = 1; i <= model->numfacetnorms; i++) {
            model->facetnorms[3 * i + 0] = -model->facetnorms[3 * i + 0];
            model->facetnorms[3 * i + 1] = -model->facetnorms[3 * i + 1];
            model->facetnorms[3 * i + 2] = -model->facetnorms[3 * i + 2];
        }

        /* reverse vertex normals */
        for (i = 1; i <= model->numnormals; i++) {
            model->normals[3 * i + 0] = -model->normals[3 * i + 0];
            model->normals[3 * i + 1] = -model->normals[3 * i + 1];
            model->normals[3 * i + 2] = -model->normals[3 * i + 2];
        }
    }

    /* glmFacetNormals: Generates facet normals for a model (by taking the
     * cross product of the two vectors derived from the sides of each
     * triangle).  Assumes a counter-clockwise winding.
     *
     * model - initialized GLMmodel structure
     */
    GLvoid
    glmFacetNormals(GLMmodel* model)
    {
        GLuint  i;
        GLfloat u[3];
        GLfloat v[3];

        assert(model);
        assert(model->vertices);

        /* clobber any old facetnormals */
        if (model->facetnorms)
            free(model->facetnorms);

        /* allocate memory for the new facet normals */
        model->numfacetnorms = model->numtriangles;
        model->facetnorms = (GLfloat*)malloc(sizeof(GLfloat) *
                           3 * (model->numfacetnorms + 1));

        for (i = 0; i < model->numtriangles; i++) {
            model->triangles[i].findex = i+1;

            u[0] = model->vertices[3 * T(i).vindices[1] + 0] -
                model->vertices[3 * T(i).vindices[0] + 0];
            u[1] = model->vertices[3 * T(i).vindices[1] + 1] -
                model->vertices[3 * T(i).vindices[0] + 1];
            u[2] = model->vertices[3 * T(i).vindices[1] + 2] -
                model->vertices[3 * T(i).vindices[0] + 2];

            v[0] = model->vertices[3 * T(i).vindices[2] + 0] -
                model->vertices[3 * T(i).vindices[0] + 0];
            v[1] = model->vertices[3 * T(i).vindices[2] + 1] -
                model->vertices[3 * T(i).vindices[0] + 1];
            v[2] = model->vertices[3 * T(i).vindices[2] + 2] -
                model->vertices[3 * T(i).vindices[0] + 2];

            glmCross(u, v, &model->facetnorms[3 * (i+1)]);
            glmNormalize(&model->facetnorms[3 * (i+1)]);
        }
    }

    /* glmVertexNormals: Generates smooth vertex normals for a model.
     * First builds a list of all the triangles each vertex is in.   Then
     * loops through each vertex in the the list averaging all the facet
     * normals of the triangles each vertex is in.   Finally, sets the
     * normal index in the triangle for the vertex to the generated smooth
     * normal.   If the dot product of a facet normal and the facet normal
     * associated with the first triangle in the list of triangles the
     * current vertex is in is greater than the cosine of the angle
     * parameter to the function, that facet normal is not added into the
     * average normal calculation and the corresponding vertex is given
     * the facet normal.  This tends to preserve hard edges.  The angle to
     * use depends on the model, but 90 degrees is usually a good start.
     *
     * model - initialized GLMmodel structure
     * angle - maximum angle (in degrees) to smooth across
     */
    GLvoid
    glmVertexNormals(GLMmodel* model, GLfloat angle)
    {
        GLMnode*    node;
        GLMnode*    tail;
        GLMnode** members;
        GLfloat*    normals;
        GLuint  numnormals;
        GLfloat average[3];
        GLfloat dot, cos_angle;
        GLuint  i, avg;

        assert(model);
        assert(model->facetnorms);

        /* calculate the cosine of the angle (in degrees) */
        cos_angle = cos(angle * M_PI / 180.0);

        /* nuke any previous normals */
        if (model->normals)
            free(model->normals);

        /* allocate space for new normals */
        model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
        model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));

        /* allocate a structure that will hold a linked list of triangle
        indices for each vertex */
        members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
        for (i = 1; i <= model->numvertices; i++)
            members[i] = NULL;

        /* for every triangle, create a node for each vertex in it */
        for (i = 0; i < model->numtriangles; i++) {
            node = (GLMnode*)malloc(sizeof(GLMnode));
            node->index = i;
            node->next  = members[T(i).vindices[0]];
            members[T(i).vindices[0]] = node;

            node = (GLMnode*)malloc(sizeof(GLMnode));
            node->index = i;
            node->next  = members[T(i).vindices[1]];
            members[T(i).vindices[1]] = node;

            node = (GLMnode*)malloc(sizeof(GLMnode));
            node->index = i;
            node->next  = members[T(i).vindices[2]];
            members[T(i).vindices[2]] = node;
        }

        /* calculate the average normal for each vertex */
        numnormals = 1;
        for (i = 1; i <= model->numvertices; i++) {
        /* calculate an average normal for this vertex by averaging the
            facet normal of every triangle this vertex is in */
            node = members[i];
            if (!node)
                fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
            average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
            avg = 0;
            while (node) {
            /* only average if the dot product of the angle between the two
            facet normals is greater than the cosine of the threshold
            angle -- or, said another way, the angle between the two
                facet normals is less than (or equal to) the threshold angle */
                dot = glmDot(&model->facetnorms[3 * T(node->index).findex],
                    &model->facetnorms[3 * T(members[i]->index).findex]);
                if (dot > cos_angle) {
                    node->averaged = GL_TRUE;
                    average[0] += model->facetnorms[3 * T(node->index).findex + 0];
                    average[1] += model->facetnorms[3 * T(node->index).findex + 1];
                    average[2] += model->facetnorms[3 * T(node->index).findex + 2];
                    avg = 1;            /* we averaged at least one normal! */
                } else {
                    node->averaged = GL_FALSE;
                }
                node = node->next;
            }

            if (avg) {
                /* normalize the averaged normal */
                glmNormalize(average);

                /* add the normal to the vertex normals list */
                model->normals[3 * numnormals + 0] = average[0];
                model->normals[3 * numnormals + 1] = average[1];
                model->normals[3 * numnormals + 2] = average[2];
                avg = numnormals;
                numnormals++;
            }

            /* set the normal of this vertex in each triangle it is in */
            node = members[i];
            while (node) {
                if (node->averaged) {
                    /* if this node was averaged, use the average normal */
                    if (T(node->index).vindices[0] == i)
                        T(node->index).nindices[0] = avg;
                    else if (T(node->index).vindices[1] == i)
                        T(node->index).nindices[1] = avg;
                    else if (T(node->index).vindices[2] == i)
                        T(node->index).nindices[2] = avg;
                } else {
                    /* if this node wasn't averaged, use the facet normal */
                    model->normals[3 * numnormals + 0] =
                        model->facetnorms[3 * T(node->index).findex + 0];
                    model->normals[3 * numnormals + 1] =
                        model->facetnorms[3 * T(node->index).findex + 1];
                    model->normals[3 * numnormals + 2] =
                        model->facetnorms[3 * T(node->index).findex + 2];
                    if (T(node->index).vindices[0] == i)
                        T(node->index).nindices[0] = numnormals;
                    else if (T(node->index).vindices[1] == i)
                        T(node->index).nindices[1] = numnormals;
                    else if (T(node->index).vindices[2] == i)
                        T(node->index).nindices[2] = numnormals;
                    numnormals++;
                }
                node = node->next;
            }
        }

        model->numnormals = numnormals - 1;

        /* free the member information */
        for (i = 1; i <= model->numvertices; i++) {
            node = members[i];
            while (node) {
                tail = node;
                node = node->next;
                free(tail);
            }
        }
        free(members);

        /* pack the normals array (we previously allocated the maximum
        number of normals that could possibly be created (numtriangles *
        3), so get rid of some of them (usually alot unless none of the
        facet normals were averaged)) */
        normals = model->normals;
        model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));
        for (i = 1; i <= model->numnormals; i++) {
            model->normals[3 * i + 0] = normals[3 * i + 0];
            model->normals[3 * i + 1] = normals[3 * i + 1];
            model->normals[3 * i + 2] = normals[3 * i + 2];
        }
        free(normals);
    }

    /* glmLinearTexture: Generates texture coordinates according to a
     * linear projection of the texture map.  It generates these by
     * linearly mapping the vertices onto a square.
     *
     * model - pointer to initialized GLMmodel structure
     */
     /*
    GLvoid
    glmLinearTexture(GLMmodel* model)
    {
        GLMgroup *group;
        GLfloat dimensions[3];
        GLfloat x, y, scalefactor;
        GLuint i;

        assert(model);

        if (model->texcoords)
            free(model->texcoords);
        model->numtexcoords = model->numvertices;
        model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));

        glmDimensions(model, dimensions);
        scalefactor = 2.0 /
            glmAbs(glmMax(glmMax(dimensions[0], dimensions[1]), dimensions[2]));

        // do the calculations
        for(i = 1; i <= model->numvertices; i++) {
            x = model->vertices[3 * i + 0] * scalefactor;
            y = model->vertices[3 * i + 2] * scalefactor;
            model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
            model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
        }

        // go through and put texture coordinate indices in all the triangles
        group = model->groups;
        while(group) {
            for(i = 0; i < group->numtriangles; i++) {
                T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
                T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
                T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
            }
            group = group->next;
        }

    #if 0
        printf("glmLinearTexture(): generated %d linear texture coordinates\n",
            model->numtexcoords);
    #endif
    }
    */

    /* glmSpheremapTexture: Generates texture coordinates according to a
     * spherical projection of the texture map.  Sometimes referred to as
     * spheremap, or reflection map texture coordinates.  It generates
     * these by using the normal to calculate where that vertex would map
     * onto a sphere.  Since it is impossible to map something flat
     * perfectly onto something spherical, there is distortion at the
     * poles.  This particular implementation causes the poles along the X
     * axis to be distorted.
     *
     * model - pointer to initialized GLMmodel structure
     */
    GLvoid
    glmSpheremapTexture(GLMmodel* model)
    {
        GLMgroup* group;
        GLfloat theta, phi, rho, x, y, z, r;
        GLuint i;

        assert(model);
        assert(model->normals);

        if (model->texcoords)
            free(model->texcoords);
        model->numtexcoords = model->numnormals;
        model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));

        for (i = 1; i <= model->numnormals; i++) {
            z = model->normals[3 * i + 0];  /* re-arrange for pole distortion */
            y = model->normals[3 * i + 1];
            x = model->normals[3 * i + 2];
            r = sqrt((x * x) + (y * y));
            rho = sqrt((r * r) + (z * z));

            if(r == 0.0) {
                theta = 0.0;
                phi = 0.0;
            } else {
                if(z == 0.0)
                    phi = 3.14159265 / 2.0;
                else
                    phi = acos(z / rho);

                if(y == 0.0)
                    theta = 3.141592365 / 2.0;
                else
                    theta = asin(y / r) + (3.14159265 / 2.0);
            }

            model->texcoords[2 * i + 0] = theta / 3.14159265;
            model->texcoords[2 * i + 1] = phi / 3.14159265;
        }

        /* go through and put texcoord indices in all the triangles */
        group = model->groups;
        while(group) {
            for (i = 0; i < group->numtriangles; i++) {
                T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
                T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
                T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
            }
            group = group->next;
        }
    }

    /* glmDelete: Deletes a GLMmodel structure.
     *
     * model - initialized GLMmodel structure
     */
    GLvoid
    glmDelete(GLMmodel* model)
    {
        GLMgroup* group;
        GLuint i;

        assert(model);

        if (model->pathname)     free(model->pathname);
        if (model->mtllibname) free(model->mtllibname);
        if (model->vertices)     free(model->vertices);
        if (model->normals)  free(model->normals);
        if (model->texcoords)  free(model->texcoords);
        if (model->facetnorms) free(model->facetnorms);
        if (model->triangles)  free(model->triangles);
        if (model->materials) {
            for (i = 0; i < model->nummaterials; i++)
                free(model->materials[i].name);
        }
        free(model->materials);
        while(model->groups) {
            group = model->groups;
            model->groups = model->groups->next;
            free(group->name);
            free(group->triangles);
            free(group);
        }

        free(model);
    }

    /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
     * Returns a pointer to the created object which should be free'd with
     * glmDelete().
     *
     * filename - name of the file containing the Wavefront .OBJ format data.
     */
    GLMmodel*
    glmReadOBJ(char* filename)
    {
        GLMmodel* model;
        FILE*   file;

        /* open the file */
        file = fopen(filename, "r");
        if (!file) {
            fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
                filename);
            exit(1);
        }

        /* allocate a new model */
        model = (GLMmodel*)malloc(sizeof(GLMmodel));
        model->pathname    = strdup(filename);
        model->mtllibname    = NULL;
        model->numvertices   = 0;
        model->vertices    = NULL;
        model->numnormals    = 0;
        model->normals     = NULL;
        model->numtexcoords  = 0;
        model->texcoords       = NULL;
        model->numfacetnorms = 0;
        model->facetnorms    = NULL;
        model->numtriangles  = 0;
        model->triangles       = NULL;
        model->nummaterials  = 0;
        model->materials       = NULL;
        model->numgroups       = 0;
        model->groups      = NULL;
        model->position[0]   = 0.0;
        model->position[1]   = 0.0;
        model->position[2]   = 0.0;

        /* make a first pass through the file to get a count of the number
        of vertices, normals, texcoords & triangles */
        glmFirstPass(model, file);

        /* allocate memory */
        model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
            3 * (model->numvertices + 1));
        model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
            model->numtriangles);
        if (model->numnormals) {
            model->normals = (GLfloat*)malloc(sizeof(GLfloat) *
                3 * (model->numnormals + 1));
        }
        if (model->numtexcoords) {
            model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) *
                2 * (model->numtexcoords + 1));
        }

        /* rewind to beginning of file and read in the data this pass */
        rewind(file);

        glmSecondPass(model, file);

        /* close the file */
        fclose(file);

        return model;
    }

    /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
     * a file.
     *
     * model - initialized GLMmodel structure
     * filename - name of the file to write the Wavefront .OBJ format data to
     * mode  - a bitwise or of values describing what is written to the file
     *             GLM_NONE     -  render with only vertices
     *             GLM_FLAT     -  render with facet normals
     *             GLM_SMOOTH   -  render with vertex normals
     *             GLM_TEXTURE  -  render with texture coords
     *             GLM_COLOR    -  render with colors (color material)
     *             GLM_MATERIAL -  render with materials
     *             GLM_COLOR and GLM_MATERIAL should not both be specified.
     *             GLM_FLAT and GLM_SMOOTH should not both be specified.
     */
    GLvoid
    glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode)
    {
        GLuint  i;
        FILE*   file;
        GLMgroup* group;

        assert(model);

        /* do a bit of warning */
        if (mode & GLM_FLAT && !model->facetnorms) {
            printf("glmWriteOBJ() warning: flat normal output requested "
                "with no facet normals defined.\n");
            mode &= ~GLM_FLAT;
        }
        if (mode & GLM_SMOOTH && !model->normals) {
            printf("glmWriteOBJ() warning: smooth normal output requested "
                "with no normals defined.\n");
            mode &= ~GLM_SMOOTH;
        }
        if (mode & GLM_TEXTURE && !model->texcoords) {
            printf("glmWriteOBJ() warning: texture coordinate output requested "
                "with no texture coordinates defined.\n");
            mode &= ~GLM_TEXTURE;
        }
        if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
            printf("glmWriteOBJ() warning: flat normal output requested "
                "and smooth normal output requested (using smooth).\n");
            mode &= ~GLM_FLAT;
        }
        if (mode & GLM_COLOR && !model->materials) {
            printf("glmWriteOBJ() warning: color output requested "
                "with no colors (materials) defined.\n");
            mode &= ~GLM_COLOR;
        }
        if (mode & GLM_MATERIAL && !model->materials) {
            printf("glmWriteOBJ() warning: material output requested "
                "with no materials defined.\n");
            mode &= ~GLM_MATERIAL;
        }
        if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
            printf("glmWriteOBJ() warning: color and material output requested "
                "outputting only materials.\n");
            mode &= ~GLM_COLOR;
        }

        /* open the file */
        file = fopen(filename, "w");
        if (!file) {
            fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
                filename);
            exit(1);
        }

        /* spit out a header */
        fprintf(file, "#  \n");
        fprintf(file, "#  Wavefront OBJ generated by GLM library\n");
        fprintf(file, "#  \n");
        fprintf(file, "#  GLM library\n");
        fprintf(file, "#  Nate Robins\n");
        fprintf(file, "#  ndr@pobox.com\n");
        fprintf(file, "#  http://www.pobox.com/~ndr\n");
        fprintf(file, "#  \n");

        if (mode & GLM_MATERIAL && model->mtllibname) {
            fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
            glmWriteMTL(model, filename, model->mtllibname);
        }

        /* spit out the vertices */
        fprintf(file, "\n");
        fprintf(file, "# %d vertices\n", model->numvertices);
        for (i = 1; i <= model->numvertices; i++) {
            fprintf(file, "v %f %f %f\n",
                model->vertices[3 * i + 0],
                model->vertices[3 * i + 1],
                model->vertices[3 * i + 2]);
        }

        /* spit out the smooth/flat normals */
        if (mode & GLM_SMOOTH) {
            fprintf(file, "\n");
            fprintf(file, "# %d normals\n", model->numnormals);
            for (i = 1; i <= model->numnormals; i++) {
                fprintf(file, "vn %f %f %f\n",
                    model->normals[3 * i + 0],
                    model->normals[3 * i + 1],
                    model->normals[3 * i + 2]);
            }
        } else if (mode & GLM_FLAT) {
            fprintf(file, "\n");
            fprintf(file, "# %d normals\n", model->numfacetnorms);
            for (i = 1; i <= model->numnormals; i++) {
                fprintf(file, "vn %f %f %f\n",
                    model->facetnorms[3 * i + 0],
                    model->facetnorms[3 * i + 1],
                    model->facetnorms[3 * i + 2]);
            }
        }

        /* spit out the texture coordinates */
        if (mode & GLM_TEXTURE) {
            fprintf(file, "\n");
            fprintf(file, "# %d texcoords\n", model->texcoords);
            for (i = 1; i <= model->numtexcoords; i++) {
                fprintf(file, "vt %f %f\n",
                    model->texcoords[2 * i + 0],
                    model->texcoords[2 * i + 1]);
            }
        }

        fprintf(file, "\n");
        fprintf(file, "# %d groups\n", model->numgroups);
        fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
        fprintf(file, "\n");

        group = model->groups;
        while(group) {
            fprintf(file, "g %s\n", group->name);
            if (mode & GLM_MATERIAL)
                fprintf(file, "usemtl %s\n", model->materials[group->material].name);
            for (i = 0; i < group->numtriangles; i++) {
                if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
                    fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
                        T(group->triangles[i]).vindices[0],
                        T(group->triangles[i]).nindices[0],
                        T(group->triangles[i]).tindices[0],
                        T(group->triangles[i]).vindices[1],
                        T(group->triangles[i]).nindices[1],
                        T(group->triangles[i]).tindices[1],
                        T(group->triangles[i]).vindices[2],
                        T(group->triangles[i]).nindices[2],
                        T(group->triangles[i]).tindices[2]);
                } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
                    fprintf(file, "f %d/%d %d/%d %d/%d\n",
                        T(group->triangles[i]).vindices[0],
                        T(group->triangles[i]).findex,
                        T(group->triangles[i]).vindices[1],
                        T(group->triangles[i]).findex,
                        T(group->triangles[i]).vindices[2],
                        T(group->triangles[i]).findex);
                } else if (mode & GLM_TEXTURE) {
                    fprintf(file, "f %d/%d %d/%d %d/%d\n",
                        T(group->triangles[i]).vindices[0],
                        T(group->triangles[i]).tindices[0],
                        T(group->triangles[i]).vindices[1],
                        T(group->triangles[i]).tindices[1],
                        T(group->triangles[i]).vindices[2],
                        T(group->triangles[i]).tindices[2]);
                } else if (mode & GLM_SMOOTH) {
                    fprintf(file, "f %d//%d %d//%d %d//%d\n",
                        T(group->triangles[i]).vindices[0],
                        T(group->triangles[i]).nindices[0],
                        T(group->triangles[i]).vindices[1],
                        T(group->triangles[i]).nindices[1],
                        T(group->triangles[i]).vindices[2],
                        T(group->triangles[i]).nindices[2]);
                } else if (mode & GLM_FLAT) {
                    fprintf(file, "f %d//%d %d//%d %d//%d\n",
                        T(group->triangles[i]).vindices[0],
                        T(group->triangles[i]).findex,
                        T(group->triangles[i]).vindices[1],
                        T(group->triangles[i]).findex,
                        T(group->triangles[i]).vindices[2],
                        T(group->triangles[i]).findex);
                } else {
                    fprintf(file, "f %d %d %d\n",
                        T(group->triangles[i]).vindices[0],
                        T(group->triangles[i]).vindices[1],
                        T(group->triangles[i]).vindices[2]);
                }
            }
            fprintf(file, "\n");
            group = group->next;
        }

        fclose(file);
    }

    /* glmDraw: Renders the model to the current OpenGL context using the
     * mode specified.
     *
     * model - initialized GLMmodel structure
     * mode  - a bitwise OR of values describing what is to be rendered.
     *             GLM_POINTS   -  render with only vertices
     *             GLM_FLAT     -  render with facet normals
     *             GLM_SMOOTH   -  render with vertex normals
     *             GLM_TEXTURE  -  render with texture coords
     *             GLM_COLOR    -  render with colors (color material)
     *             GLM_MATERIAL -  render with materials
     *             GLM_COLOR and GLM_MATERIAL should not both be specified.
     *             GLM_FLAT and GLM_SMOOTH should not both be specified.
     */
    GLvoid
    glmDraw(GLMmodel* model, GLuint mode)
    {
        static GLuint i;
        static GLMgroup* group;
        static GLMtriangle* triangle;
        static GLMmaterial* material;

        assert(model);
        assert(model->vertices);

        /* do a bit of warning */
        if (mode & GLM_FLAT && !model->facetnorms) {
            printf("glmDraw() warning: flat render mode requested "
                "with no facet normals defined.\n");
            mode &= ~GLM_FLAT;
        }
        if (mode & GLM_SMOOTH && !model->normals) {
            printf("glmDraw() warning: smooth render mode requested "
                "with no normals defined.\n");
            mode &= ~GLM_SMOOTH;
        }
        if (mode & GLM_TEXTURE && !model->texcoords) {
            printf("glmDraw() warning: texture render mode requested "
                "with no texture coordinates defined.\n");
            mode &= ~GLM_TEXTURE;
        }

    	if (mode & GLM_TEXTURE)			//@RG
    		glEnable(GL_TEXTURE_2D);

        if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
            printf("glmDraw() warning: flat render mode requested "
                "and smooth render mode requested (using smooth).\n");
            mode &= ~GLM_FLAT;
        }
        if (mode & GLM_COLOR && !model->materials) {
            printf("glmDraw() warning: color render mode requested "
                "with no materials defined.\n");
            mode &= ~GLM_COLOR;
        }
        if (mode & GLM_MATERIAL && !model->materials) {
            printf("glmDraw() warning: material render mode requested "
                "with no materials defined.\n");
            mode &= ~GLM_MATERIAL;
        }
        if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
            printf("glmDraw() warning: color and material render mode requested "
                "using only material mode.\n");
            mode &= ~GLM_COLOR;
        }
        if (mode & GLM_COLOR)
            glEnable(GL_COLOR_MATERIAL);
        else if (mode & GLM_MATERIAL)
            glDisable(GL_COLOR_MATERIAL);

        /* perhaps this loop should be unrolled into material, color, flat,
           smooth, etc. loops?  since most cpu's have good branch prediction
           schemes (and these branches will always go one way), probably
           wouldn't gain too much?  */

        group = model->groups;
        while (group) {
            if (mode & GLM_MATERIAL) {
                material = &model->materials[group->material];
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material->ambient);
                glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material->diffuse);
                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material->specular);
                glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material->shininess);
            }

            if (mode & GLM_COLOR) {
                glColor3fv(material->diffuse);
            }
            if(mode & GLM_POINTS)
    			glBegin(GL_POINTS);
    		else
    			glBegin(GL_TRIANGLES);

            for (i = 0; i < group->numtriangles; i++) {
                triangle = &T(group->triangles[i]);

                if (mode & GLM_FLAT)
                    glNormal3fv(&model->facetnorms[3 * triangle->findex]);

                if (mode & GLM_SMOOTH)
                    glNormal3fv(&model->normals[3 * triangle->nindices[0]]);
                if (mode & GLM_TEXTURE)
                    glTexCoord2fv(&model->texcoords[2 * triangle->tindices[0]]);
                glVertex3fv(&model->vertices[3 * triangle->vindices[0]]);

                if (mode & GLM_SMOOTH)
                    glNormal3fv(&model->normals[3 * triangle->nindices[1]]);
                if (mode & GLM_TEXTURE)
                    glTexCoord2fv(&model->texcoords[2 * triangle->tindices[1]]);
                glVertex3fv(&model->vertices[3 * triangle->vindices[1]]);

                if (mode & GLM_SMOOTH)
                    glNormal3fv(&model->normals[3 * triangle->nindices[2]]);
                if (mode & GLM_TEXTURE)
                    glTexCoord2fv(&model->texcoords[2 * triangle->tindices[2]]);
                glVertex3fv(&model->vertices[3 * triangle->vindices[2]]);

            }
            glEnd();

            group = group->next;
        }
    	if (mode & GLM_TEXTURE)			//@RG
    		glDisable(GL_TEXTURE_2D);
    }

    /* glmList: Generates and returns a display list for the model using
     * the mode specified.
     *
     * model - initialized GLMmodel structure
     * mode  - a bitwise OR of values describing what is to be rendered.
     *             GLM_NONE     -  render with only vertices
     *             GLM_FLAT     -  render with facet normals
     *             GLM_SMOOTH   -  render with vertex normals
     *             GLM_TEXTURE  -  render with texture coords
     *             GLM_COLOR    -  render with colors (color material)
     *             GLM_MATERIAL -  render with materials
     *             GLM_COLOR and GLM_MATERIAL should not both be specified.
     * GLM_FLAT and GLM_SMOOTH should not both be specified.
     */
    GLuint
    glmList(GLMmodel* model, GLuint mode)
    {
        GLuint list;

        list = glGenLists(1);
        glNewList(list, GL_COMPILE);
        glmDraw(model, mode);
        glEndList();

        return list;
    }

    /* glmWeld: eliminate (weld) vectors that are within an epsilon of
     * each other.
     *
     * model   - initialized GLMmodel structure
     * epsilon     - maximum difference between vertices
     *               ( 0.00001 is a good start for a unitized model)
     *
     */
    GLvoid
    glmWeld(GLMmodel* model, GLfloat epsilon)
    {
        GLfloat* vectors;
        GLfloat* copies;
        GLuint   numvectors;
        GLuint   i;

        /* vertices */
        numvectors = model->numvertices;
        vectors  = model->vertices;
        copies = glmWeldVectors(vectors, &numvectors, epsilon);

    #if 1
        printf("glmWeld(): %d redundant vertices.\n",
            model->numvertices - numvectors - 1);
    #endif

        for (i = 0; i < model->numtriangles; i++) {
            T(i).vindices[0] = (GLuint)vectors[3 * T(i).vindices[0] + 0];
            T(i).vindices[1] = (GLuint)vectors[3 * T(i).vindices[1] + 0];
            T(i).vindices[2] = (GLuint)vectors[3 * T(i).vindices[2] + 0];
        }

        /* free space for old vertices */
        free(vectors);

        /* allocate space for the new vertices */
        model->numvertices = numvectors;
        model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
            3 * (model->numvertices + 1));

        /* copy the optimized vertices into the actual vertex list */
        for (i = 1; i <= model->numvertices; i++) {
            model->vertices[3 * i + 0] = copies[3 * i + 0];
            model->vertices[3 * i + 1] = copies[3 * i + 1];
            model->vertices[3 * i + 2] = copies[3 * i + 2];
        }

        free(copies);
    }

    /* glmReadPPM: read a PPM raw (type P6) file.  The PPM file has a header
     * that should look something like:
     *
     *    P6
     *    # comment
     *    width height max_value
     *    rgbrgbrgb...
     *
     * where "P6" is the magic cookie which identifies the file type and
     * should be the only characters on the first line followed by a
     * carriage return.  Any line starting with a # mark will be treated
     * as a comment and discarded.   After the magic cookie, three integer
     * values are expected: width, height of the image and the maximum
     * value for a pixel (max_value must be < 256 for PPM raw files).  The
     * data section consists of width*height rgb triplets (one byte each)
     * in binary format (i.e., such as that written with fwrite() or
     * equivalent).
     *
     * The rgb data is returned as an array of unsigned chars (packed
     * rgb).  The malloc()'d memory should be free()'d by the caller.  If
     * an error occurs, an error message is sent to stderr and NULL is
     * returned.
     *
     * filename   - name of the .ppm file.
     * width      - will contain the width of the image on return.
     * height     - will contain the height of the image on return.
     *
     */
    GLubyte*
    glmReadPPM(char* filename, int* width, int* height)
    {
        FILE* fp;
        int i, w, h, d;
        unsigned char* image;
        char head[70];          /* max line <= 70 in PPM (per spec). */

        fp = fopen(filename, "rb");
        if (!fp) {
            perror(filename);
            return NULL;
        }

        /* grab first two chars of the file and make sure that it has the
           correct magic cookie for a raw PPM file. */
        fgets(head, 70, fp);
        if (strncmp(head, "P6", 2)) {
            fprintf(stderr, "%s: Not a raw PPM file\n", filename);
            return NULL;
        }

        /* grab the three elements in the header (height, width, maxval). */
        i = 0;
        while(i < 3) {
            fgets(head, 70, fp);
            if (head[0] == '#')     /* skip comments. */
                continue;
            if (i == 0)
                i += sscanf(head, "%d %d %d", &h, &w, &d);
            else if (i == 1)
                i += sscanf(head, "%d %d", &w, &d);
            else if (i == 2)
                i += sscanf(head, "%d", &d);
        }

        /* grab all the image data in one fell swoop. */
        image = (unsigned char*)malloc(sizeof(unsigned char)*w*h*3);
        fread(image, sizeof(unsigned char), w*h*3, fp);
        fclose(fp);

        *width = w;
        *height = h;
        return image;
    }

    #if 0
    /* normals */
    if (model->numnormals) {
        numvectors = model->numnormals;
        vectors  = model->normals;
        copies = glmOptimizeVectors(vectors, &numvectors);

        printf("glmOptimize(): %d redundant normals.\n",
            model->numnormals - numvectors);

        for (i = 0; i < model->numtriangles; i++) {
            T(i).nindices[0] = (GLuint)vectors[3 * T(i).nindices[0] + 0];
            T(i).nindices[1] = (GLuint)vectors[3 * T(i).nindices[1] + 0];
            T(i).nindices[2] = (GLuint)vectors[3 * T(i).nindices[2] + 0];
        }

        /* free space for old normals */
        free(vectors);

        /* allocate space for the new normals */
        model->numnormals = numvectors;
        model->normals = (GLfloat*)malloc(sizeof(GLfloat) *
            3 * (model->numnormals + 1));

        /* copy the optimized vertices into the actual vertex list */
        for (i = 1; i <= model->numnormals; i++) {
            model->normals[3 * i + 0] = copies[3 * i + 0];
            model->normals[3 * i + 1] = copies[3 * i + 1];
            model->normals[3 * i + 2] = copies[3 * i + 2];
        }

        free(copies);
    }

    /* texcoords */
    if (model->numtexcoords) {
        numvectors = model->numtexcoords;
        vectors  = model->texcoords;
        copies = glmOptimizeVectors(vectors, &numvectors);

        printf("glmOptimize(): %d redundant texcoords.\n",
            model->numtexcoords - numvectors);

        for (i = 0; i < model->numtriangles; i++) {
            for (j = 0; j < 3; j++) {
                T(i).tindices[j] = (GLuint)vectors[3 * T(i).tindices[j] + 0];
            }
        }

        /* free space for old texcoords */
        free(vectors);

        /* allocate space for the new texcoords */
        model->numtexcoords = numvectors;
        model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) *
            2 * (model->numtexcoords + 1));

        /* copy the optimized vertices into the actual vertex list */
        for (i = 1; i <= model->numtexcoords; i++) {
            model->texcoords[2 * i + 0] = copies[2 * i + 0];
            model->texcoords[2 * i + 1] = copies[2 * i + 1];
        }

        free(copies);
    }
    #endif

    #if 0
    /* look for unused vertices */
    /* look for unused normals */
    /* look for unused texcoords */
    for (i = 1; i <= model->numvertices; i++) {
        for (j = 0; j < model->numtriangles; i++) {
            if (T(j).vindices[0] == i ||
                T(j).vindices[1] == i ||
                T(j).vindices[1] == i)
                break;
        }
    }
    #endif

    ¡¡

    file name: chopper.cpp

    //////////////////////////////////////////////////////////////////////////////////////////
    // Chopper_V2: Chopper target shooting game
    // Author [EAZ = Elias Abou Zeid I.D:4792637]	e_abouze@ece.concordia.ca
    // Date: Dec-9-2005
    //////////////////////////////////////////////////////////////////////////////////////////

    #include <glut.h>
    #include <stdio.h>
    #include <math.h>
    #include "texture.h"
    #include "Environment.h"
    #include "glm.h"

    /******************************************** VARIABLES DECLARATIONS *********************/

    #pragma comment( lib, "glut32" )
    #pragma comment( lib, "glaux" )

    //Texture related
    #define NUM_OF_TEXTURES 18						// The number of textures you are going to load in this program.
    GLuint  g_Texture[NUM_OF_TEXTURES];				// Stores the texture handles as returned by OpenGL
    bool bTexFlag;									// Stores whether the texture were loaded successfully or not.

    Environment Game_env;

    #define NUM_OF_SOUNDS 6
    Sound	g_Sound[NUM_OF_SOUNDS];
    bool bMuteALL;

    //////////////////////////////////////////////////////////////////////////////////////////

    GLMmodel* targetModelLists[MaxTargetCount];
    //GLMmodel* fragmentModelLists[MaxFragmentCount];
    //GLMmodel* numberModelLists[10];
    GLMmodel* missileModelLists[MaxMissileModelCount];

    GLMmodel*  singleBuildingModel;
    //GLMmodel*  manyBuildingModel;

    const int TargetFileCount=2;
    char* singleBuildingFileName="data/single_Building.obj";
    //char* manyBuildingFileName="data/many_buildings.obj";

    char* targetFileNameLists[TargetFileCount]=
    {
    	"data/tank.obj",
    	"data/plane.obj"
    };

    GLfloat singleBuildingCoordLists[MaxSingleBuildingCount][3];

    /*
    GLfloat manyBuildingCoord[3]=
    {
    	50, FLOOR_LEVEL, 50
    };
    */

    GLfloat targetCoordLists[MaxTargetCount][3]=
    {
    	{0, FLOOR_LEVEL, 5},
    	{-5, FLOOR_LEVEL, -15},
    	{3, FLOOR_LEVEL, 35},
    	{0, FLOOR_LEVEL, 10},
    	{0, FLOOR_LEVEL, 15},
    	{3, FLOOR_LEVEL, 20},
    	{2, TargLevel+5, -15},
    	{-5, TargLevel-1, 30},
    	{-10, TargLevel+3, -10},
    	{2, TargLevel+10, 25},
    	{-5, TargLevel+1, 15},
    	{-10, TargLevel+3, 10}
    };

    void openFire(DWORD now, int x, int y);
    extern Movement movement;
    extern Rocket rockets[MaxRocketCount];
    extern int rocketCount;
    extern int targetCount;
    extern int explosionCount;
    extern int explosionIndex;
    extern Rocket explosions[MaxExplosionPieces];
    extern TargetStruct targets[MaxTargetCount];
    bool boundaryCheck(float fx, float fy, float fz, const Movement& mv=movement);
    void createExplosion(float coord[3], DWORD now);
    void drawExplosionPiece(int index);
    void drawExplosion(DWORD now);
    //void loadFragments();
    bool loadNumbers();
    void loadMissile();
    void loadBuilding();
    void drawBuilding();
    void drawDigit(int num, int pos, int start);
    void drawNumber(int number);
    void drawScoreBoard();
    ////////////////////////////////////////////////////////////////////////////////////////////

    //float yaw, pitch, roll;	// For the FP camera.

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

    bool bWire;			// To wireframe/not.

    bool bSpin;			// To spin/stop spining
    bool bCircular;		// To stop/start motion light 2.
    bool IsStart;   // did game start?

    /*lights */
    bool bLight1;
    bool bLight2;
    bool bLight3;

    float fovy = FOVY;		// For Perspective projections.
    float fViewVol=FVIEWVOL;			// For Orthographic projections.
    int H,W; //keep track of window sizing

    float mainrot_spin;  // The rotation angle for the main rotors.
    float tailrot_spin; // The rotation angle for the tail rotors.

    float light2_circular; // The circular rotation angle for light 2.

    float radius,theta,phi; //for camera motion.

    //GL_Window			window;
    DWORD				tickCount;										// Used For The Tick Counter

    GLdouble eqn[4] = {0.0, 1.0, 0.0, 0.0}; //clip y=0-plane
    GLdouble eqn1[4] = {0.0, 0.0, -1.0, 0.2}; //clip z=0.2-palne, remove the right hand side
    GLdouble eqn2[4] = {0.0, 0.0, 1.0, -0.2}; //clip y=0.2-plan, rmeove the left hand side

    /********************************************NEW:Timing (Day/Night) **************************/
    void NightTime(void)
    {
    	Game_env.day = NIGHT; //it's night  time

    	glEnable(GL_LIGHTING);

    	if(!bMuteALL && !g_Sound[5].mute)
    	PlaySound(g_Sound[5].filename , NULL, SND_ASYNC);

    	g_Sound[3].mute=false;
    }

    void DayTime(void)
    {
    	Game_env.day = DAY; //it's night  time
    	Game_env.weather  = SHINY_DAY; //set day to a shiny day
    	Game_env.amdif = HIGH;  //set ambiant and diffuse to high

    	glDisable(GL_LIGHTING); //no lighting during day
    	if(!bMuteALL && !g_Sound[4].mute)
    	PlaySound(g_Sound[4].filename, NULL, SND_ASYNC);

    	g_Sound[3].mute =false;
    };

    void WindSound(void) //play when no rotor sound
    {
    	if((!bSpin || g_Sound[1].mute) && !g_Sound[3].mute && !bMuteALL)
    	{
    		PlaySound(NULL,NULL,NULL); //stop previous sound
    		PlaySound(g_Sound[3].filename, NULL, SND_ASYNC);
    		g_Sound[3].mute=true;
    	}
    }

    void Timer(GLfloat time) //timing to retrieve ellapsed time
    {
    	GLfloat time_ellapsed= time - Game_env.timeday;

        if( time_ellapsed > HDAY)
            {
    			if(Game_env.day == DAY)
    				NightTime();
    			else if(Game_env.day == NIGHT)
    				DayTime();

    			Game_env.timeday = time;

    		}

    	else if(time_ellapsed > HDAY/2)
    		WindSound();
    }

    void drawTargets(DWORD time_elapsed);

    void loadTargets();
    /****************************************************************************************/
    //NeHeGL library

    GLfloat		rolly;

    //HDC			hDC=NULL;		// Private GDI Device Context												// Rolling Clouds												// Structure Name

    void Update(DWORD milliseconds)
    {
    	rolly -=milliseconds*0.00005f;	// Roll The Clouds
    }

    //start announcement
    void drawAnnounce(int Texindex)
    {
    	glEnable(GL_TEXTURE_2D);	// enable Texture Mapping
    	//glColor4f(0.3, 0.3, 0.3, 1.0);
    	glColor4f(Game_env.amdif, Game_env.amdif, Game_env.amdif, 1.0);
    							// This command is important as the current color largely decides the color of the texture rendered.
    							// To keep the original texture intact we need to set the current color to WHITE.

    	// Bind the texture stored at the zero index of g_Texture[]
    	glBindTexture(GL_TEXTURE_2D, g_Texture[Texindex]);

    	glBegin(GL_POLYGON);
    	/*
    	glTexCoord2f(1.0,0.0); glVertex3f(-4.0,1.0,0.0);
    	glTexCoord2f(0.0,0.0); glVertex3f(+4.0,1.0,0.0);
    	glTexCoord2f(0.0,1.0); glVertex3f(+4.0,3.0,0.0);
    	glTexCoord2f(1.0,1.0); glVertex3f(-4.0,3.0,0.0);

    	*/
    	glTexCoord2f(1.0,0.0); glVertex3f(-2.0, FLOOR_LEVEL+1.5, 3.0);
    	glTexCoord2f(0.0,0.0); glVertex3f(+2.0, FLOOR_LEVEL+1.5, 3.0);
    	glTexCoord2f(0.0,1.0); glVertex3f(+2.0, FLOOR_LEVEL+ 3.0, 3.0);
    	glTexCoord2f(1.0,1.0); glVertex3f(-2.0, FLOOR_LEVEL+ 3.0, 3.0);
    	glEnd();
    	glDisable(GL_TEXTURE_2D);

    }

    void drawTexturedCube(GLfloat side) //draw a cube from -side/2 to side/2
    {
    /*
    	glPushMatrix();	// Push The Modelview Matrix

    	for(int i = 0; i<xslices; i++)
    	{
    	    for(int j=0; j<yslices; j++)
            {
    			GLfloat di = floor(side/xslices);
    			GLfloat dj = floor(side/yslices);
    			GLfloat du = di/side;
    			GLfloat dv = dj/side;
    			//back side at z = -side/2
    			glBegin(GL_QUADS);											// Begin Drawing Quads
    			glTexCoord2f(i*du,j*dv);			glVertex3f(-side/2+i*di,-side/2+j*dj,-side/2);	// buttom left
    			glTexCoord2f(du+i*du,j*dv);			glVertex3f(-side/2+(i+1)*di,-side/2+j*dj,-side/2);	// buttom right
    			glTexCoord2f(du+i*du,dv+j*dv);		glVertex3f(-side/2+(i+1)*di,-side/2+(j+1)*dj,-side/2);// top right
    			glTexCoord2f(i*du,dv+j*dv);			glVertex3f(-side/2+(i*di),-side/2+(j+1)*dj,-side/2);// top left
    			glEnd();

    			//front side at z = +side/2
    			glBegin(GL_QUADS);											// Begin Drawing Quads
    			glTexCoord2f(i*du,j*dv);			glVertex3f(-side/2+i*di,-side/2+j*dj,+side/2);	// buttom left
    			glTexCoord2f(du+i*du,j*dv);			glVertex3f(-side/2+(i+1)*di,-side/2+j*dj,+side/2);	// buttom right
    			glTexCoord2f(du+i*du,dv+j*dv);		glVertex3f(-side/2+(i+1)*di,-side/2+(j+1)*dj,+side/2);// top right
    			glTexCoord2f(i*du,dv+j*dv);			glVertex3f(-side/2+(i*di),-side/2+(j+1)*dj,+side/2);// top left
    			glEnd();

    			//left side at x = +side/2
    			/*glBegin(GL_QUADS);											// Begin Drawing Quads
    			glTexCoord2f(i*du,j*dv);			glVertex3f(side/2,-side/2+j*dj,-side/2+i*di);	// buttom left
    			glTexCoord2f(du+i*du,j*dv);			glVertex3f(-side/2,-side/2+j*dj,-side/2+(i+1)*di);	// buttom right
    			glTexCoord2f(du+i*du,dv+j*dv);		glVertex3f(-side/2,-side/2+(j+1)*dj,-side/2+(i+1)*di);// top right
    			glTexCoord2f(i*du,dv+j*dv);			glVertex3f(-side/2,-side/2+(j+1)*dj,-side/2+(i*di));// top left
    			glEnd();
    		}
    	}*/

    	/*//left side at x = +side/2
    			glBegin(GL_QUADS);											// Begin Drawing Quads
    			glTexCoord2f(i*di,j*dj);			glVertex3f(+side/2,-side/2+j*dj,+side/2);	// buttom left
    			glTexCoord2f(di+i*di,j*dj);			glVertex3f(+side/2,-side/2+j*dj,+side/2);	// buttom right
    			glTexCoord2f(di+i*di,dj+j*dj);		glVertex3f(+side/2,-side/2+(j+1)*dj,+side/2);// top right
    			glTexCoord2f(i*di,dj+j*dj);			glVertex3f(+side/2,-side/2+(j+1)*dj,+side/2);// top left
    			glEnd();

    	glBegin(GL_QUADS);
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-FLOOR_SIZE/2.0f,+97.5f,-FLOOR_SIZE/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+FLOOR_SIZE/2.0f,+97.5f,-FLOOR_SIZE/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+FLOOR_SIZE/2.0f,+97.5f,+FLOOR_SIZE/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-FLOOR_SIZE/2.0f,+97.5f,+FLOOR_SIZE/2.0f);	// Bottom Right	// Bottom Right
    	glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-FLOOR_SIZE/2.0,-10.0,+FLOOR_SIZE/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+FLOOR_SIZE/2.0f,-10.0f,+FLOOR_SIZE/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+FLOOR_SIZE/2.0f,+97.5,+FLOOR_SIZE/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-FLOOR_SIZE/2.0f,+97.5f,+FLOOR_SIZE/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-FLOOR_SIZE/2.0,-10.0,-FLOOR_SIZE/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(-FLOOR_SIZE/2.0f,-10.0f,+FLOOR_SIZE/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(-FLOOR_SIZE/2.0f,+97.5,+FLOOR_SIZE/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-FLOOR_SIZE/2.0f,+97.5f,-FLOOR_SIZE/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(+FLOOR_SIZE/2.0,-10.0,-FLOOR_SIZE/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+FLOOR_SIZE/2.0f,-10.0f,+FLOOR_SIZE/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+FLOOR_SIZE/2.0f,+97.5,+FLOOR_SIZE/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(+FLOOR_SIZE/2.0f,+97.5f,-FLOOR_SIZE/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-FLOOR_SIZE/2.0,-10.0f,-FLOOR_SIZE/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+FLOOR_SIZE/2.0f,-10.0f,-FLOOR_SIZE/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+FLOOR_SIZE/2.0f,-10.0f,+FLOOR_SIZE/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-FLOOR_SIZE/2.0f,-10.0f,+FLOOR_SIZE/2.0f);	// Bottom Right
        glEnd();

        glPopMatrix();*/

    	glPushMatrix();												// Push The Modelview Matrix
    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-side/2.0f,-10.0f,-side/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+side/2.0f,-10.0f,-side/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+side/2.0f,side/2 + 10.0,-side/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-side/2.0f,side/2 + 10.0,-side/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-side/2.0f,side/2+10.0,-side/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+side/2.0f,side/2+10.0,-side/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+side/2.0f,side/2+10.0,+side/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-side/2.0f,side/2+10.0,+side/2.0f);	// Bottom Right	// Bottom Right
    	glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-side/2.0,-10.0,+side/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+side/2.0f,-10.0f,+side/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+side/2.0f,side/2+10.0,+side/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-side/2.0f,side/2+10.0,+side/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-side/2.0,-10.0,-side/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(-side/2.0f,-10.0f,+side/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(-side/2.0f,side/2+10.0,+side/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-side/2.0f,side/2+10.0,-side/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(+side/2.0,-10.0,-side/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+side/2.0f,-10.0f,+side/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+side/2.0f,side/2+10.0,+side/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(+side/2.0f,side/2+10.0,-side/2.0f);	// Bottom Right
        glEnd();

    	glBegin(GL_QUADS);											// Begin Drawing Quads
    		glTexCoord2f(1.0f,rolly/1.5f+1.0f); glVertex3f(-side/2.0,-10.0f,-side/2.0f);	// Top Right
    		glTexCoord2f(0.0f,rolly/1.5f+1.0f); glVertex3f(+side/2.0f,-10.0f,-side/2.0f);	// Top Left
    		glTexCoord2f(0.0f,rolly/1.5f+0.0f); glVertex3f(+side/2.0f,-10.0f,+side/2.0f);	// Bottom Left
    		glTexCoord2f(1.0f,rolly/1.5f+0.0f); glVertex3f(-side/2.0f,-10.0f,+side/2.0f);	// Bottom Right
        glEnd();

        glPopMatrix();

    }

    /****************************************************************************************/
    void drawGun(void)
    {
    	//gun-hand
    	glBegin(GL_POLYGON);
    	{
    		glColor4f(Game_env.amdif,Game_env.amdif,Game_env.amdif,Game_env.amdif);
    		glVertex3f(0.3,-0.03,0.25);
    		glVertex3f(0.45,-0.04,0.25);
    		glVertex3f(0.45,-0.04,0.30);
    		glVertex3f(0.3,-0.03,0.30);
    	}
    	glEnd();

    	//gun base
    	glPushMatrix();
    	glTranslatef(0.45,-0.04,0.2);
    	//glRotatef(-15.0,0.0,1.0,0.0);
    	drawCylinder(0.04,0.04,0.15,20,20);
    	glPopMatrix();

    	glPushMatrix();
    	glTranslatef(0.445,-0.04,0.22);
    	drawSphere(0.043,20,20);
    	glPopMatrix();

    	glPushMatrix();
    	glTranslatef(0.445,-0.04,0.34);

    	drawSphere(0.0445,20,20);
    	glPopMatrix();

    	//gun
    	glPushMatrix();
    	glTranslatef(0.445,-0.04,0.27);
    	drawCylinder(0.01,0.005,0.25,20,20);
    	glPopMatrix();
    }

    void drawCockpit(void)
    {

    	glPushMatrix();

    	glScalef(1.0,1.5,2.0);//translate by (1.0,1.0,0.0)
    	glClipPlane (GL_CLIP_PLANE0, eqn); //clip plane at y=0, remove down side
    	glEnable (GL_CLIP_PLANE0);
    	glClipPlane (GL_CLIP_PLANE1, eqn1); //clip plane at z=0.2, remove the right hand side
    	glEnable (GL_CLIP_PLANE1);
    	glPushMatrix();
    	glRotatef(180.0,0.0,1.0,0.0);

    	glEnable(GL_TEXTURE_2D);	// enable Texture Mapping
    	glColor4f(Game_env.amdif, Game_env.amdif, Game_env.amdif, 1.0);
    							// This command is important as the current color largely decides the color of the texture rendered.
    							// To keep the original texture intact we need to set the current color to WHITE.

    	// Bind the texture stored at the zero index of g_Texture[]
    	glBindTexture(GL_TEXTURE_2D, g_Texture[0]);

    	drawSphere(0.45, 100, 100);//draw a wire sphere with radius 0.25, with 15 divisions around z-axis and 15 divisions along z-axis.
    	glPopMatrix();
    	glDisable (GL_CLIP_PLANE0);
    	glDisable (GL_CLIP_PLANE1);

    	//cover-down
    	glPushMatrix();
    	glTranslatef(0.0,-0.01,0.0);
    	glScalef(1.0,0.20,1.0);
    	drawSphere(0.45, 100, 100);
    	glPopMatrix();

    	//left-gun
    	glBindTexture(GL_TEXTURE_2D, g_Texture[0]);
    	drawGun();

    	//right-gun
    	glPushMatrix();
    	glScalef(-1.0,1.0,1.0);
    	drawGun();
    	glPopMatrix();

    	//front window
    	glDisable(GL_TEXTURE_2D);	// disable Texture Mapping
    	glEnable (GL_BLEND);
    	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    	glDepthMask(GL_FALSE);
    	glColor4f(1.0, 1.0,1.0,0.3);
    	glClipPlane (GL_CLIP_PLANE2, eqn2); //clip plane at z=-0.2, remove the left hand side
    	glEnable (GL_CLIP_PLANE2);
    	glEnable (GL_CLIP_PLANE0);
    	drawSphere(0.45, 30, 30);//draw a wire sphere with radius 0.45, with 30 divisions around z-axis and 30 divisions along z-axis.
    	glDisable (GL_CLIP_PLANE0);
    	glDisable (GL_CLIP_PLANE2);
    	glColor3f(0.4, 0.5, 0.4);
    	glDisable (GL_BLEND);
    	glDepthMask(GL_TRUE);

    	glPopMatrix();
    }

    /****************************************************************************************/
    void drawLegs(void)
    {
      glPushAttrib(GL_COLOR_BUFFER_BIT);
      glEnable(GL_TEXTURE_2D);	// enable Texture Mapping

      glColor4f(Game_env.amdif, Game_env.amdif, Game_env.amdif, 1.0);
      // Bind the texture stored at the zero index of g_Texture[]
      glBindTexture(GL_TEXTURE_2D, g_Texture[0]);

    	//front legs
    	glPushMatrix();
    		glTranslatef(0.0,-0.09,0.0);
    		glRotatef(90.0,1.0,0.0,0.0);
     		glPushMatrix();
    		glTranslatef(-0.125,0.4,0.0);
    		glRotatef(-45.0,0.0,1.0,0.0);
    		drawCylinder(0.02,0.02,0.2,5.0,5.0);
    		glPopMatrix();
    		glPushMatrix();
    		glTranslatef(0.125,0.4,0.0);
    		glRotatef(45.0,0.0,1.0,0.0);
    		drawCylinder(0.02,0.02,0.2,5.0,5.0);
    		glPopMatrix();
    	glPopMatrix();

    	//back wheel leg
    	glPushMatrix();
    		glTranslatef(0.0,0.0,-1.91);
    		glRotatef(90.0,1.0,0.0,0.0);
    		drawCylinder(0.02,0.02,0.17,5.0,5.0);
    	glPopMatrix();

    	// back Wheel leg support
    	glPushMatrix();
    		glTranslatef(0.0,0.0,-1.65);
    		glRotatef(45.0,1.0,0.0,0.0);
    		glRotatef(90.0,1.0,0.0,0.0);
    		drawCylinder(0.02,0.02,0.28,5.0,5.0);
    	glPopMatrix();

    	glDisable(GL_TEXTURE_2D);

    	glPopAttrib();

    }

    /****************************************************************************************/
    void drawFrontWheels ( bool Bwire)
    {
    	//front wheels:
    	if(Bwire)
    	{

    		glPushMatrix();
    			glTranslatef(-0.251,-0.30,0.4);
    			glRotatef(90,1.0,0.0,0.0);
    			glRotatef(90,0.0,1.0,0.0);
    			glutWireTorus(0.04, 0.08, 10, 10);
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(0.251,-0.30,0.4);
    			glRotatef(90,1.0,0.0,0.0);
    			glRotatef(90,0.0,1.0,0.0);
    			glutWireTorus(0.04, 0.08, 10, 10);
    		glPopMatrix();
    	}
    	else
    	{

    		glPushMatrix();
    			glTranslatef(-0.251,-0.30,0.4);
    			glRotatef(90,1.0,0.0,0.0);
    			glRotatef(90,0.0,1.0,0.0);
    			glutSolidTorus(0.04, 0.08, 10, 10);
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(0.251,-0.30,0.4);
    			glRotatef(90,1.0,0.0,0.0);
    			glRotatef(90,0.0,1.0,0.0);
    			glutSolidTorus(0.04, 0.08, 10, 10);
    		glPopMatrix();
    	}
    }

    /****************************************************************************************/
    void drawBackWheel( bool Bwire)
    {
    	if(Bwire)
    	{
    		glPushMatrix();
    			glTranslatef(0.0,-0.32,-1.9);
    			glRotatef(90,1.0,0.0,0.0);
    			glRotatef(90,0.0,1.0,0.0);
    			glutWireTorus(0.06, 0.1, 10, 10);
    		glPopMatrix();
    	}
    	else
    	{
    		glPushMatrix();
    			glTranslatef(0.0,-0.27,-1.9);
    			glRotatef(90,1.0,0.0,0.0);
    			glRotatef(90,0.0,1.0,0.0);
    			glutSolidTorus(0.06, 0.1, 10, 10);
    		glPopMatrix();
    	}
    }

    /****************************************************************************************/
    void drawWheels(void)
    {
      glPushAttrib(GL_COLOR_BUFFER_BIT);

      glColor4f(0.0,0.0,0.0,1.0);

      //front wheels
      drawFrontWheels(bWire);

      //back wheel
      drawBackWheel(bWire);

      glPopAttrib();

    }

    /****************************************************************************************/
    void drawTail(void)
    {
    	glEnable(GL_TEXTURE_2D);	// enable Texture Mapping
    	glColor4f(Game_env.amdif, Game_env.amdif, Game_env.amdif, 1.0);

    	// Bind the texture stored at the zero index of g_Texture[]
    	glBindTexture(GL_TEXTURE_2D, g_Texture[0]);

    	glPushMatrix();
    		glTranslatef(0.0,0.0,-0.87);
    		glRotatef(180,0.0,1.0, 0.0); //rotate by 180deg. around y-axis
    		glEnable (GL_CLIP_PLANE0); //clip at y=0, remove negative y part.
    		drawCylinder(0.15, 0.15/4, 1.1, 30.0, 30.0);
    		glDisable (GL_CLIP_PLANE0);//disable clipping
    		glScalef(1.0,0.15,1.0);
    		drawCylinder(0.15, 0.15/4, 1.1, 30.0, 30.0);
    	glPopMatrix();
    	glDisable(GL_TEXTURE_2D);
    }

    /****************************************************************************************/
    void drawTailHead(void)
    {
    	glPushAttrib(GL_COLOR_BUFFER_BIT);			// Use GL_ALL_ATTRIB_BITS if you are not sure of the attribute
    												// bits which are going to be modified
    		glColor3f(Game_env.amdif, Game_env.amdif, Game_env.amdif);				// This steps ensures that the current color is set to white.
    												// It is done to ensure that a different current color doesn't mix up with the
    												// texture's original colors.

    		// Below we draw a texture mapped square. Remember, GL_QUADS draws a 4 point polygon.
    		// In order to assign a texture map to a polygon, we need to call glBindTexture().
    		// This passes in the type of texture map (always use GL_TEXTURE_2D) and the index
    		// into our generated texture array - g_Texture[].  If we want to use another
    		// texture map, and we have multiple loaded, we just change the index into the array.

    		glEnable(GL_TEXTURE_2D);
    		// Bind the texture stored at the zero index of g_Texture[]
    		glBindTexture(GL_TEXTURE_2D, g_Texture[0]);

    		// Display a quad texture to the screen
    		glBegin(GL_QUADS);

    		// glTexCoord2f() takes the X and Y offset (or U and V) into the bitmap.
    		// Then, the next point sent to be rendered attaches that part of the bitmap
    		// to itself.  The (U, V) coordinates range from (0, 0) being the top left corner
    		// of the bitmap, to (1, 1) being the bottom left corner of the bitmap.
    		// You can go above 1 but it just is wrapped around back to zero and repeats the texture.
    		// Try setting the 1's to 2's and see what it does, then try setting them to 0.5's.
    		// The higher the number, the more instances of the texture will appear on the square,
    		// Where the lower the number, it stretches the incomplete texture over the surface of the square.
    		// For every vertice we need a U V coordinate, as shown below.  You might have to play
    		// around with the values to make it texture correctly, otherwise it will be flipped, upside down,
    		// or skewed.  It also depends on where you are looking at it.  We are looking down the -Z axis.

    			// Display the top left vertice
    			if(bTexFlag)
    				glTexCoord2f(0.0f, 0.0f);
    			glVertex3f(0.0,0.0, -1.96);

    			// Display the bottom left vertice
    			if(bTexFlag)
    				glTexCoord2f(10.0f, 0.0f);
    			glVertex3f(0.0,0.0, -1.80);

    			// Display the bottom right vertice
    			if(bTexFlag)
    				glTexCoord2f(10.0f, 10.0f);
    			glVertex3f(0.0,0.45, -1.960);

    			// Display the top right vertice
    			if(bTexFlag)
    				glTexCoord2f(0.0f, 10.0f);
    			glVertex3f(0.0,0.45, -2.03);

    		glEnd();
    			glDisable(GL_TEXTURE_2D);

    	glPopAttrib();
    }

    /****************************************************************************************/
    void drawTailRotors(bool Bwire)
    {
    	glPushAttrib(GL_COLOR_BUFFER_BIT);
    		if(bSpin)
    		{
    			glEnable (GL_BLEND);
    			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    			glDepthMask(GL_FALSE);

    			for(int i =0; i<90;i++) // motion blur alpha blending/blade acummulation
    			{
    				glPushMatrix();
    				glColor4f(0.0,0.0,0.0,1.0 -0.2*float(i));

    				//1st rotor
    					glTranslatef(0.05,0.35,-1.97);
    					glRotatef(tailrot_spin + 3.0*float(i),1.0,0.0,0.0);
    					glTranslatef(-0.05,-0.35,+1.97);
    				glBegin(GL_POLYGON);
    						glVertex3f(0.05, 0.05,-1.95);
    						glVertex3f(0.05, 0.05,-1.99);
    						glVertex3f(0.05, 0.65,-1.99);
    						glVertex3f(0.05, 0.65,-1.95);
    				glEnd();

    				//2nd rotor
    					glTranslatef(0.05,0.35,-1.97);
    					glRotatef(tailrot_spin + 3.0*float(i),1.0,0.0,0.0);
    					glTranslatef(-0.05,-0.35,+1.97);

    				glBegin(GL_POLYGON);
    						glVertex3f(0.05, 0.33,-1.67);
    						glVertex3f(0.05, 0.33,-2.29);
    						glVertex3f(0.05, 0.37,-2.29);
    						glVertex3f(0.05, 0.37,-1.67);
    				glEnd();
    				glPopMatrix();

    			}
    			glDisable (GL_BLEND);
    			glDepthMask(GL_TRUE);

    		}
    		else
    		{
    			//1st rotor
    			glPushMatrix();
    				glTranslatef(0.05,0.35,-1.97);
    				glRotatef(tailrot_spin,1.0,0.0,0.0);
    				glTranslatef(-0.05,-0.35,+1.97);
    			glBegin(GL_POLYGON);
    					glVertex3f(0.05, 0.05,-1.95);
    					glVertex3f(0.05, 0.05,-1.99);
    					glVertex3f(0.05, 0.65,-1.99);
    					glVertex3f(0.05, 0.65,-1.95);
    			glEnd();

    			glBegin(GL_POLYGON);
    					glVertex3f(0.05, 0.33,-1.67);
    					glVertex3f(0.05, 0.33,-2.29);
    					glVertex3f(0.05, 0.37,-2.29);
    					glVertex3f(0.05, 0.37,-1.67);
    			glEnd();
    			glPopMatrix();
    		}
    }

    /****************************************************************************************/
    void drawTailRotorsBase(void)
    {
    	//draw back head
    	glPushAttrib(GL_COLOR_BUFFER_BIT);
    	glColor4f(Game_env.amdif,Game_env.amdif,Game_env.amdif,1.0);

    	glEnable(GL_TEXTURE_2D);
    		// Bind the texture stored at the zero index of g_Texture[]
    		glBindTexture(GL_TEXTURE_2D, g_Texture[0]);
    	 glPushMatrix();
    		glTranslatef(0.0,0.35,-1.97);
    		glRotatef(90,0.0,1.0, 0.0); //rotate by 90deg. around y-axis
    		drawCylinder(0.03, 0.001, 0.10, 5.0, 5.0); //draw a cylinder with base radius 0.3, up radius 0.001, height 0.1, with 5 divisions around z-axis and 5 divisions along z-axis.
    	glPopMatrix();
    	glDisable(GL_TEXTURE_2D);
    	glPopAttrib();

    }

    /****************************************************************************************/
    void drawChopperEnd(void)
    {
    	//Draw tail
    	drawTail();

    	//Draw tail head
    	drawTailHead();

    }

    /****************************************************************************************/
    void drawMainRotors(bool Bwire)
    {
    		glPushAttrib(GL_COLOR_BUFFER_BIT);
    		if(bSpin)
    		{
    			glEnable (GL_BLEND);
    			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    			glDepthMask(GL_FALSE);

    			glPushMatrix();
    			glRotatef(mainrot_spin,0.0,1.0,0.0);
    			for (int i=0; i<90; i++) //motion blur effect: Alpha Blending/blades accumulation
    				{
    					glColor4f(0.0, 0.0,0.0,1.0 - (0.2*i));
    			   		glPushMatrix();
    						glRotatef(3.0*float(i),0.0,1.0,0.0);
    						glTranslatef(0.0,0.71,0.0); //translate by (0.0,0.71,0.0)
    						glPushMatrix();
    						glScalef(0.33, 0.00,7);

    						if(Bwire)
    							glutWireCube(0.25);
    						else  glutSolidCube(0.25);

    						glPopMatrix();
    						glRotatef(90.0 + 3.0*float(i),0.0,1.0,0.0);
    						glScalef(0.33, 0.00,7);

    						if(Bwire)
    							glutWireCube(0.25);
    						else  glutSolidCube(0.25);

    					glPopMatrix();
    				}
    		   glPopMatrix();

    		   glDisable (GL_BLEND);
    		   glDepthMask(GL_TRUE);

    		   glPopAttrib();
    		}
    		else
    		{
    			glPushAttrib(GL_COLOR_BUFFER_BIT);
    			glColor4f(0.0,0.0,0.0,1.0);

    			glPushMatrix();
    			glTranslatef(0.0,0.71,0.0); //translate by (0.0,0.71,0.0)
    			glPushMatrix();
    			glScalef(0.3, 0.00,7);

    			if(Bwire)
    				glutWireCube(0.25);
    			else  glutSolidCube(0.25);

    			glPopMatrix();
    			glRotatef(90.0,0.0,1.0,0.0);
    			glScalef(0.3, 0.00,7);

    			if(Bwire)
    				glutWireCube(0.25);
    			else  glutSolidCube(0.25);

    			glPopMatrix();
    		}
    	glPopAttrib();
    }

    /****************************************************************************************/
    void drawHead(void)
    {
      glPushAttrib(GL_COLOR_BUFFER_BIT);
      glEnable(GL_TEXTURE_2D);	// enable Texture Mapping

      glColor4f(Game_env.amdif, Game_env.amdif, Game_env.amdif, 1.0);

      // Bind the texture stored at the zero index of g_Texture[]
      glBindTexture(GL_TEXTURE_2D, g_Texture[0]);

    	glPushMatrix();
    		glTranslatef(0.0,0.65,0.0); //translate by (0.0,0.65,0.0)
    		glRotatef(-90,1.0,0.0, 0.0); //rotate by -90deg. around x-axis
    		drawCylinder(0.03, 0.0, 0.15, 5.0, 5.0);
    	glPopMatrix();

      glDisable(GL_TEXTURE_2D); //disable texture mapping
      glPopAttrib();

    };

    void shinyLight()
    {
    	if(Game_env.IsShiny)
    	{

    		GLfloat matSpec[4] = {1.0, 1.0, 1.0, 1.0};
    		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpec);
    		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0);
    		Game_env.amdif=LOW; //put a low diffuse and ambiant to emphasis specular(shiny, presence)
    	}
    	else
        {
    		Game_env.amdif=HIGH;
    		GLfloat matSpec[4] = {0.0, 0.0, 0.0, 0.0};
    		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpec);
    		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 00.0);
    	}

    }
    /****************************************************************************************/

    /****************************************************************************************/
    void drawChopper(void)
    {

    	glPushAttrib(GL_ALL_ATTRIB_BITS);

    		glPushMatrix();
    		//glTranslatef(0.0,HecLevel,0.55);
    		//step1: draw chopper skeleton
    		/***********************/
    		drawChopperEnd(); //draw back part

    		//step2: draw accescories
    		/***********************/
    		drawHead(); //draw head

    		drawTailRotorsBase();//Draw rotors base
    		drawLegs(); //draw legs
    		drawWheels(); //draw wheels

    		//step3: draw blended parts
    		/**************************/
    		drawCockpit(); //draw cockpit
    		drawMainRotors(bWire);	//Draw main rotors
    		drawTailRotors(bWire); //Draw tail rotors
    		glPopMatrix();
    	glPopAttrib();
    }

    /************* NEW *****************/

    void Draw3DSGrid(int w, int h)
    //	 Draws a tesselated 3D grid. Can control its tesselation as an input parameter.
    //   Finer tesselation, better lighting, but slower performance.
    {
      int i, j;
      float dw = FLOOR_SIZE / w;
      float dh = FLOOR_SIZE / h;
      GLfloat du = dw/FLOOR_SIZE;
      GLfloat dv = dh/FLOOR_SIZE;

      glColor3f(Game_env.amdif,Game_env.amdif,Game_env.amdif);
      glPushMatrix();
      glTranslatef(-FLOOR_SIZE/2, -2.5, -FLOOR_SIZE/2);
      glNormal3f(0.0, 1.0, 0.0);

       glEnable(GL_TEXTURE_2D);
    		// Bind the texture stored at the zero index of g_Texture[]
    	glBindTexture(GL_TEXTURE_2D, g_Texture[1]);

      for (j = 0; j < h; ++j) {
        glBegin(GL_TRIANGLE_STRIP);
        for (i = 0; i <= w; ++i) {
    	    glTexCoord2f(du * i, dv * (j + 1));
    		glVertex3f(dw * i, 0.0, dh * (j + 1));
    	    //glVertex3f(dw * i, 0*sin(i*PI/180.0), dh * (j + 1));

    		glTexCoord2f(du * i, dv * j);
    		//glVertex3f(dw * i, 0*sin(i*PI/180.0), dh * j);
    		glVertex3f(dw * i, 0.0, dh * j);
        }
        glEnd();
      }
      glPopMatrix();
      glDisable(GL_TEXTURE_2D);
    }
    /****************************************************************************************/
    void drawChopperBase()
    {
    	glPushAttrib(GL_COLOR_BUFFER_BIT);

    	glEnable(GL_TEXTURE_2D);	// enable Texture Mapping
    	glColor4f(Game_env.amdif, Game_env.amdif, Game_env.amdif, 1.0);
    							// This command is important as the current color largely decides the color of the texture rendered.
    							// To keep the original texture intact we need to set the current color to WHITE.

    	 // Bind the texture stored at the index of g_Texture[]
    	 glBindTexture(GL_TEXTURE_2D, g_Texture[3]);

    	//draw square
    	glBegin(GL_QUADS);
    		glTexCoord2f(0.0f,0.0f); glVertex3f(2.0,-2.459,-2.0);
    		glTexCoord2f(1.0f,0.0f); glVertex3f(-2.0,-2.459,-2.0);
    		glTexCoord2f(1.0f,1.0f); glVertex3f(-2.0,-2.459,+2.0);
    		glTexCoord2f(0.0f,1.0f);  glVertex3f(+2.0,-2.459,+2.0);
        glEnd();

       glDisable(GL_TEXTURE_2D);
    }

    /****************************************************************************************/
    void drawChopperBases()
    {
    	glColor3f(1.0,0.2,0.2);
    	//drawBaseAt(2.0);
    	//drawBaseAt(-2.0);
    	drawChopperBase();

    }
    /****************************************************************************************/
    void drawSkyballon(void)
    {
    	glColor3f(1.0,1.0,0.0);
    	glPushMatrix();
    	glTranslatef(0.0,0.2,20.0);
    	glScalef(0.6,0.8,0.7);
    	drawSphere(0.20,10.0,10.0);
    	glColor3f(0.0,0.0,0.0);
    	glBegin(GL_LINES);
    		glVertex3f(0.0,0.0,0.0);
    		glVertex3f(0.0,-1,0.0);
    	glEnd();
    	glPopMatrix();

    	glColor3f(0.0,1.0,0.0);
    	glPushMatrix();
    	glTranslatef(0.25,0.2,20.0);
    	glScalef(0.6,0.8,0.7);
    	drawSphere(0.20,10.0,10.0);
    	glColor3f(0.0,0.0,0.0);
    	glBegin(GL_LINES);
    		glVertex3f(0.15,0.0,0.0);
    		glVertex3f(-0.6,-1,0.0);
    	glEnd();
    	glPopMatrix();

    	glColor3f(0.0,0.0,1.0);
    	glPushMatrix();
    	glTranslatef(-0.25,0.2,20.0);
    	glScalef(0.6,0.8,0.7);
    	drawSphere(0.20,10.0,10.0);
    	glColor3f(0.0,0.0,0.0);
    		glBegin(GL_LINES);
    		glVertex3f(-0.15,0.0,0.0);
    		glVertex3f(+0.6,-1.0,0.0);
    	glEnd();
    	glPopMatrix();
    }
    /****************************************************************************************/
    void drawParaBombs()
    {
    	glPushMatrix();
    	glTranslatef(0.0,TargLevel,0.0);
    	drawParaBombAt(0.0,3.0);
    	glPopMatrix();
    }

    /****************************************************************************************/
    void drawSky()
    {
    	glPushAttrib(GL_ALL_ATTRIB_BITS);
        if(Game_env.weather == FOGY_DAY && Game_env.day == DAY)
    	glColor4f(0.5,0.5,0.5,1.0); //fogy sky color
    	else glColor4f(1.0,1.0,1.0,1.0);

    	if(Game_env.weather == SHINY_DAY || Game_env.day == NIGHT)
    		glEnable(GL_TEXTURE_2D);	// Enable Texture Mapping, only if its is shiny day or night otherwise, fog effect

    	if(Game_env.day == DAY)
    	glBindTexture(GL_TEXTURE_2D, g_Texture[2]);			// Select day Sky Texture

    	else if(Game_env.day == NIGHT)
    			glBindTexture(GL_TEXTURE_2D, g_Texture[5]);	// Select night Sky Texture

    	glPushMatrix();
    	glScalef(1.0,1.0,0.75);
        drawTexturedCube(6*FLOOR_SIZE);
    	glPopMatrix();

    	glDisable(GL_TEXTURE_2D);	// Disable Texture Mapping

    	glPopAttrib();
    }
    /****************************************************************************************/
    void drawEnvironment(void)
    {
    	Draw3DSGrid(100,100);
    	drawParaBombs();
    	drawGroundTrees();
    	drawChopperBases();
    	drawSkyballon();
    	drawSky();
    }

    /****************************************************************************************/
    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(1, 0, 0);
    						// Y axis green
          glColor3f(0, 1, 0);
          glVertex3f(0, 0, 0);
          glVertex3f(0, 2, 0);
    						// Z axis blue
          glColor3f(0, 0, 2);
          glVertex3f(0, 0, 0);
          glVertex3f(0, 0, 2);
    	  glEnd();
    	  glPopAttrib();
    						// // Pops attributes like current color from the attribute stack and sets as current.
    	}
    }

    /****************************************************************************************/
    void showMenu(void)
    {
    	printf("\n\n ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////");
    	printf("\n OpenGL PROGRAM FOR DRAWING A HELICOPTER AND OPERATING ON OT\n");
    	printf("\n OCTOBER, 15 2005 \n");
    	printf("////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    	printf("\n\n Operations: ");
    	printf("\n Use a/A keys to show/hide reference axis projection.");
    	printf("\n\n Use P/p and N/n to switch between orthogonal and perspective view. ");
    	printf("\n Use UP/DOWN arrow to move chopper forward/backward at current heading direction.");
    	printf("\n Use LEFT/RIGHT arrow to move chopper at current left/right-hand-side direction.");
    	printf("\n Use the f/b,keys to move third-person-view camera forward/backward.");
    	printf("\n Use w/W keys to toggle between wire frame/shaded model.");
    	printf("\n Use o/O or n/N key to toggle between orthographic and perspective projection.");
    	printf("\n Use z/Z key to zoom in and out respectively.");
    	printf("\n Use c/C key to clear viewing parameters and cheat to win!.");
    	printf("\n Use s/S to toggle between spin on/off rotors.");
    	printf("\n Use 1/3 key to toggle between 1st and 3rd person view.");
    	printf("\n Use y/Y key for a Yaw rotation.");
    	printf("\n Use p/P key for a pitch rotation.");
    	printf("\n Use i/I and k/K to move rotate the center of projection up/down about the center of gravity of chopper.");
    	printf("\n Use l/L to turn on/off sound effect.");
    	printf("\n Use F1 to turn on/off light 1.");
    	printf("\n Use F2 to turn on/off light 2.");
    	printf("\n Use 6/7 rotate third-person-view camera around chopper horizontally.");
    	printf("\n Use 8/9 rotate third-person-view camera around chopper vertically.");

    	printf("\n Use U/u/D/d to move chopper upward/downward vertically");
    	printf("\n Click mouse at first-person-view mode for firing rockets");

    	printf("\n Use F4 to toggle the material property rusty/shiny");
    	printf("\n Use 2 to turn on/off motion of light 2.");
    	printf("\n Use h/H to control low/high beam of light 1.");
    	printf("\n Use n/M to mute/play rotor sounds.");
    	printf("\n Use x or M to see this menu again.");
    	printf("\n Use esc key to exit gracefully from program.");
    }

    /******************** NEW ****************************/

    // For and light properties.

    GLfloat green[] = { 0.4, 1.0, 0.4, 1.0 };
    GLfloat red[] = {1.0,0.0,0.0,1.0};
    GLfloat blue[]={0.0,0.0,1.0,1.0};

    GLfloat lightAmbient[] = { 0.3, 1.0, 0.4, 1.0 };

    GLfloat lightone_position[] = { 0.0, 0.0, 0.8, 1.0 };
    GLfloat lightone_direction[] = { 0.0,-2, 10.0 };
    GLfloat fSpotLightone = 30.0;
    GLfloat fExpLightone =2.0;

    GLfloat lighttwo_position[] = { 1.0, 5.0, 0.0, 1.0 };
    GLfloat lighttwo_direction[] = { 0.0, -1.0, 0.0 };
    GLfloat fSpotLighttwo = 15.0;

    //GLfloat lightthr_position[] = { 0.0, 0.0, 0.0, 1.0 };
    //GLfloat lightthr_direction[] = { 0.0, 0.0, -1.0 };
    //GLfloat fSpotLightthr = 30.0;

    void initLightOne(void)
    {
    	/* Initializtion of light 1 */
    	glLightfv(GL_LIGHT0, GL_AMBIENT, red);
    	glLightfv(GL_LIGHT0, GL_DIFFUSE, red);
    	glLightfv(GL_LIGHT0, GL_SPECULAR, red);
    	glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, fSpotLightone);
    	glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, fExpLightone);
    	glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0);
    	glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0);
    	glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0);
    };

    void initLightTwo(void)
    {
    	/* Initializtion of light 2 */
    	glLightfv(GL_LIGHT1, GL_AMBIENT, blue);
    	glLightfv(GL_LIGHT1, GL_DIFFUSE, blue);
    	glLightfv(GL_LIGHT1, GL_SPECULAR, blue);
    	glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, fSpotLighttwo);
    	glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2.0);
    	glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0);
    	glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0);
    	glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0);
    };

    /*
    void initLightThree(void)
    {
    	// Initializtion of light 3
    	glLightfv(GL_LIGHT2, GL_AMBIENT, green);
    	glLightfv(GL_LIGHT2, GL_DIFFUSE, green);
    	glLightfv(GL_LIGHT2, GL_SPECULAR, green);
    	glLightf(GL_LIGHT2, GL_SPOT_CUTOFF, fSpotLightthr);
    	glLightf(GL_LIGHT2, GL_SPOT_EXPONENT, 2.0);
    	glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, 1.0);
    	glLightf(GL_LIGHT2, GL_LINEAR_ATTENUATION, 0.0);
    	glLightf(GL_LIGHT2, GL_QUADRATIC_ATTENUATION, 0.0);
    };
    */

    void initLights(void)
    					// Initialize the common lights related data.
    {
    	initLightOne();
    	initLightTwo();
    //	initLightThree();

    }

    /****************************************** SOUND EFFECT RELATED ***************************/

    void RotorSound(int w) //set action taken when image is idle no interaction from user.
    {
    	if(bSpin && !g_Sound[1].mute && !bMuteALL)
    	{
    		if(Game_env.camera == THIRD_PERSON)
    		{
    		  PlaySound(g_Sound[1].filename, NULL, SND_ASYNC);
    		  glutTimerFunc(g_Sound[1].duration,RotorSound, 10);
    		}
    	}
    	else {

    		PlaySound(NULL, NULL, NULL); //kill rotor sound directly
    	}

    }

    void BattleSound(int w) //set this sound to play when game is not started or game over
    {
    	if(!IsStart && !bMuteALL)
    	{
    		  PlaySound(g_Sound[0].filename, NULL, SND_ASYNC);
    		  glutTimerFunc(g_Sound[0].duration,BattleSound, 10);

    	}
    	else
    	{
    		PlaySound(NULL,NULL,NULL);
    		g_Sound[0].mute = true;
    	}

    }
    /****************************************************************************************/

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

       //initialization of game environment environment
       Game_env.camera		  = THIRD_PERSON;
       Game_env.day			  = DAY;
       Game_env.projection	  = PERSPECTIVE;
       Game_env.weather		  = SHINY_DAY;
       Game_env.IsShiny		  = false; //is the material shiny
       Game_env.amdif	      = HIGH; //set diffuse and ambiant material property to high
       Game_env.timeday       = 0.0;
       Game_env.lastTickCount = GetTickCount();

      //initializing all light to on
    	bLight1 = true;
    	bLight2 = true;
    	bLight3 = true;

    	bTexFlag  =  false;

    	bMuteALL  = false;

    	IsStart = false; //user did not start the game yet

    	glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lightAmbient) ;

    	//Yaw,Pitch,Roll initialization
    	//yaw = 0;
    	//pitch = 0;
    	//roll = 0;
    	initializeMovement();

    	loadTargets();
    	//loadFragments();

    	loadMissile();
    	//loadBuilding();
    	//SPin angle initialization
    	mainrot_spin = 0.0;
    	tailrot_spin = 0.0;

    	//Light 2Circular angle initialization
    	light2_circular = 0.0;

    	//camera position initialisation
    	theta = -70;
    	phi = 0;
    	radius = 4.0;

    	W = H = SIZE;		// global variables used to keep track of window size.

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

    	glClearColor (1.0, 1.0, 1.0, 1.0); //make bacground color sky color

    	glShadeModel(GL_SMOOTH);
    	glEnable(GL_DEPTH_TEST);// Enable z-Buffering.
    	bAxis = false;
    	bWire = false;
    	bSpin = false;
    	bCircular = false;
    	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    	/****************** NEW ************************/
    	// Initialize lighting and enable color material switch so that all the glColor values are
    	//	taken in as AMBIENT AND DIFFUSE coefficients. Other material properties would be 0.
    	initLights();

    	glEnable(GL_LIGHT0);
    	glEnable(GL_LIGHT1);
    	glEnable(GL_LIGHT2);
    	glEnable(GL_NORMALIZE);

    	glEnable(GL_COLOR_MATERIAL);
    	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

    	//texture:
    	if(!bTexFlag)
    	{
    		if( !CreateTexture("Data/Alumplat.bmp",g_Texture, 0) ||
    			!CreateTexture("Data/SnowTexture.bmp", g_Texture, 1)||
    			!CreateTexture("Data/Sky.bmp", g_Texture, 2)  ||
    			!CreateTexture("Data/base.bmp", g_Texture, 3) ||
    			!CreateTexture("Data/start.bmp", g_Texture, 4) ||
    			!CreateTexture("Data/sky_night.bmp", g_Texture, 5)||
    			!CreateTexture("Data/target.bmp", g_Texture, 6)||
    			!CreateTexture("Data/winner.bmp", g_Texture, 7)//||!loadNumbers()

    		  )
    			bTexFlag = false;	// Load the texture into memory. Remember you can only provide BMP files here.
    		else bTexFlag = true;
    	}

    	//sounds
    	g_Sound[0].filename = "Data/battle_start.wav";
    	g_Sound[0].duration = 12000;
    	g_Sound[0].mute		= false;

    	g_Sound[1].filename = "Data/blakhawk.wav";
    	g_Sound[1].duration = 5000;
    	g_Sound[1].mute		= false;

    	g_Sound[2].filename = "Data/choppergun.wav";
    	g_Sound[2].duration = 0.00;
    	g_Sound[2].mute		= false;

    	g_Sound[3].filename = "Data/wind.wav";
    	g_Sound[3].duration = 7000;
    	g_Sound[3].mute		= false;

    	g_Sound[4].filename = "Data/G_Rooster_01.wav";
    	g_Sound[4].duration = 1000;
    	g_Sound[4].mute		= false;

    	g_Sound[5].filename = "Data/Frog01.wav";
    	g_Sound[5].duration = 1000;
    	g_Sound[5].mute		= false;

    	g_Sound[6].filename = "Data/explosion.wav";
    	g_Sound[6].duration = 2000;
    	g_Sound[6].mute		= false;

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

    	showMenu();
    	BattleSound(10); //start the battle sound before user start egine.
    }
    /****************************************************************************************/

    void Motion(void) //set action taken when image is idle no interaction from user.
    {

    	if(bSpin)
    	{
    		tailrot_spin+=60;
    		mainrot_spin += 75;

    		if(IsStart && (movement.y < Flight_Latitude))
    		{

                movement.y +=0.05;
    		}
    		else if(IsStart) //chopper ready to shoot
            {
    			IsStart=false;
    			//TargLevel -= 0.05;
    		}

    		Timer(GetTickCount());
    	}

    	if(bCircular)
    	{
    		light2_circular += 2.0;
    	}

    	glutPostRedisplay();
    }
    /************************************************/
    void keyboardCallbackProc(unsigned char key, int x, int y)
    						// This is the callback procedure for capturing OpenGL Keyboard events.
    {
    	float fx, fy, fz;
    	Movement temp=movement;
    	switch(key)
    	{

    	case 'a':				// Show axes.
    	case 'A':
    		if (bAxis == true)			bAxis = false;
    		else						bAxis = true;
    		break;
    	case 'z': //zoom in
    		fovy -= 0.5;
    		fViewVol -= 0.2;
    		break;
    	case 'Z'://zoom out
    		fovy += 0.5;
    		fViewVol += 0.2;
    		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 'N':				//Change to perspective projections
    	case 'n':
    		Game_env.projection = PERSPECTIVE;
    		break;

    	case 'O':				//Change to orthographic projections
    	case 'o':
    		Game_env.projection = ORTHOGRAPHIC;
    		break;

    	case 'F': //move camera position forward
    	case 'f':
    		   //if(abs(radius - 0.1) > 0.0)
    		   //{
    			   radius -= 0.1;
    		   //}
    		break;

    	case 'b': //move camera position backward
    	case 'B':
    		radius += 0.1;
    		break;
    	case 'c':
    	case 'C':
    		if(Game_env.camera == FIRST_PERSON)
    		{
    			fovy = FIRSTVIEWFOVY;
    			fViewVol = FIRSTFVIEWVOL;
    		}else if(Game_env.camera == THIRD_PERSON)
    		{

    			fovy = FOVY;
    			fViewVol=FVIEWVOL;
    		}
    		initializeMovement();
    		targetCount=0;

    		break;
    	case 's' :
    	case 'S' :
    		if(IsStart == 0)
    		{
    			IsStart = !IsStart;
    			Game_env.timeday=GetTickCount(); //start timer
    		}

    		bSpin = !bSpin;
    		RotorSound(10);
    		break;
        case 'L' :
    	case 'l' :
    		bMuteALL = !bMuteALL;
    		PlaySound(NULL,NULL,NULL); //stip any sound being played
    		RotorSound(10);
    		break;
    	case 'M' :
    	case 'm' :
    		g_Sound[1].mute= !g_Sound[1].mute;
    		RotorSound(10);
    		break;
    	case 'X' :
    	case 'x' :
    		showMenu();
    		break;
    	case 'p':
    	//	pitch += 0.1;
    		//pitch has some boundary limit
    		if (movement.pitch>-45)
    		{
    			temp.pitch-=AngleOffset;
    			if (boundaryCheck(0, 0, 0, temp))
    			{
    				movement.pitch-=AngleOffset;
    			}
    		}
    		break;
    	case 'P':
    		if (movement.pitch<45)
    		{
    			temp.pitch+=AngleOffset;
    			if (boundaryCheck(0, 0, 0, temp))
    			{
    				movement.pitch+=AngleOffset;
    			}
    		}
    	//	pitch -= 0.1;
    		break;
    	case 'y':
    		//yaw += 0.1;
    		//for yaw, there should be no boundary
    	//	if (movement.yaw<90)
    		//if (movement.yaw<90)
    		{
    			temp.yaw+=AngleOffset;
    			if (boundaryCheck(0, 0, 0, temp))
    			{
    				movement.yaw+=AngleOffset;

    				if (movement.yaw>360)
    				{
    					movement.yaw-=360;
    				}

    			}
    		}
    		break;
    	case 'Y':
    		//if (movement.yaw>-90)
    		{
    			temp.yaw-=AngleOffset;
    			if (boundaryCheck(0, 0, 0, temp))
    			{
    				movement.yaw-=AngleOffset;

    				if (movement.yaw<-360)
    				{
    					movement.yaw+=360;
    				}

    			}
    		}
    		//yaw -= 0.1;
    		break;
    	case 'r':
    		if (movement.roll>-45)
    		{
    			movement.roll-=AngleOffset;
    		}
    	//	roll += 0.1;
    		break;
    	case 'R':
    		if (movement.roll<45)
    		{
    			movement.roll+=AngleOffset;
    		}
    	//	roll -= 0.1;
    		break;

    	case '1' :
    		Game_env.camera = FIRST_PERSON; //switch to first person view
    		fovy = FIRSTVIEWFOVY;
    		fViewVol = FIRSTFVIEWVOL;
    		PlaySound(NULL, NULL, NULL); //kill sound directly
    		break;
    	case '3' :
    		Game_env.camera = THIRD_PERSON; //switch to third person view
    		fovy = FOVY;
    		fViewVol = FVIEWVOL;
    		RotorSound(10);
    		break;
    	case 'i':
    	case'I':
    		theta -= AngleOffset;
    		break;
    	case 'k':
    	case'K':
    		theta += AngleOffset;
    		break;
    	case 'j':
    	case'J':
    		phi -= AngleOffset;
    		break;
    	case 't':
    	case'T':
    		phi += AngleOffset;
    		break;
    	/****************** NEW ************************/
    	case 'h':
    		lightone_direction[1]= -0.5;
    		lightone_direction[2]= 1.5;
    		fSpotLightone = 20.0;
    		fExpLightone =10.0;
    		initLightOne();
    		break;

    	case 'H':
    		lightone_direction[1]= -2.0;
    		lightone_direction[2]= 10.0;
    		fSpotLightone = 30.0;
    		fExpLightone =2.0;
    		initLightOne();
    		break;

    	case '9':
    		theta+=1;
    		break;
    	case '8':
    		theta-=1;
    		break;
    	case '7':
    		phi+=1;
    		break;
    	case '6':
    		phi-=1;
    		break;
    	case '2':
    		bCircular = !bCircular;
    		//light2_circular += 5.0;
    		break;

    	/******************************************/
    	case 'U':
    	case 'u':
    		temp.pitch+=90;
    		calcCoord(PositionOffset, temp, fx, fy, fz);
    		if (boundaryCheck(fx, fy, fz))
    		{
    			movement.x+=fx;
    			movement.y+=fy;
    			movement.z+=fz;
    		}
    		break;
    	case 'D':
    	case 'd':
    		temp.pitch-=90;
    		calcCoord(PositionOffset, temp, fx, fy, fz);
    		if (boundaryCheck(fx, fy, fz))
    		{
    			movement.x+=fx;
    			movement.y+=fy;
    			movement.z+=fz;
    		}
    		break;
    	case '4':

    		break;

    	case 27 :				//ESCAPE Code for exiting program.
    		exit(0);
    	}
    	glutPostRedisplay();
    }

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

    void specialCallbackProc (int key, int x, int y)
    						// This is the callback procedure for capturing special charater events.
    {
    	float fx, fy, fz;
    	Movement temp=movement;
    	switch (key)
    	{
    		case GLUT_KEY_LEFT:		// Rotate center of projection left
    			//phi -= 0.5;
    			temp.yaw+=90;
    			calcCoord(PositionOffset, temp, fx, fy, fz);
    			if (boundaryCheck(fx, fy, fz))
    			{
    				movement.x+=fx;
    				movement.y+=fy;
    				movement.z+=fz;
    			}
    			break;

    		case GLUT_KEY_RIGHT:	// Rotate center of projection right
    			temp.yaw-=90;
    			calcCoord(PositionOffset, temp, fx, fy, fz);
    			if (boundaryCheck(fx, fy, fz))
    			{
    				movement.x+=fx;
    				movement.y+=fy;
    				movement.z+=fz;
    			}
    			break;

    		case GLUT_KEY_UP :		// Rotate center of projection up
    			//theta -= 0.5;
    			calcCoord(MovementOffset, movement, fx, fy, fz);
    			if (boundaryCheck(fx, fy, fz))
    			{
    				movement.x+=fx;
    				movement.y+=fy;
    				movement.z+=fz;
    			}
    			//movement.z+=1;
    			break;

    		case GLUT_KEY_DOWN :	// Rotate center of projection down
    			//theta += 0.5;
    			//movement.z-=1;
    			calcCoord(-MovementOffset, movement, fx, fy, fz);
    			if (boundaryCheck(fx, fy, fz))
    			{
    				movement.x+=fx;
    				movement.y+=fy;
    				movement.z+=fz;
    			}
    			break;
    		case GLUT_KEY_F1:
    			bLight1 = !bLight1;
    			if(!bLight1)
    				glDisable(GL_LIGHT0);
    			else glEnable(GL_LIGHT0);
    			break;
    		case GLUT_KEY_F2:
    			bLight2 = !bLight2;
    			if(!bLight2)
    				glDisable(GL_LIGHT1);
    			else glEnable(GL_LIGHT1);
    			break;
    		case GLUT_KEY_F3:
    			bLight3 = !bLight3;
    			if(!bLight3)
    				glDisable(GL_LIGHT2);
    			else glEnable(GL_LIGHT2);
    			break;

    		case GLUT_KEY_F4:
    			Game_env.IsShiny = !Game_env.IsShiny;
    			shinyLight(); //set on/off shiny/rusty

    			if(Game_env.day == DAY) // PUT A FOGGY/SHINY DAY
    			{
    				if(Game_env.weather == SHINY_DAY)
    				{
    					Game_env.weather = FOGY_DAY;
    					Game_env.amdif   = LOW;
    				}
                    else
    				{
    					Game_env.weather = SHINY_DAY;
    					Game_env.amdif   = HIGH;
    				}

    			}
    			break;
    	}
    	glutPostRedisplay();
    }

    void Mouse(int button, int state, int x, int y)
    {
    	if(button == GLUT_LEFT_BUTTON)
        {
    		if(state == GLUT_DOWN)
            {
    			if(Game_env.camera == FIRST_PERSON && !g_Sound[2].mute && !bMuteALL)
    			{
    				PlaySound(g_Sound[2].filename, NULL, SND_ASYNC);
    				openFire(GetTickCount(), x, y);
    			}
    		}
    	}
    }

    /****************************************************************************************/
    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;  //keep track of window resizing
    	H = h;

    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	if(Game_env.projection == PERSPECTIVE)
    		gluPerspective(fovy, (GLfloat)W/(GLfloat)H, NEAR_Z, FAR_Z);
    	else if(Game_env.projection == ORTHOGRAPHIC)
    	{
    		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);

    }

    /****************************************************************************************/
    void displayCallbackProc (void)
    						// This is the callback procedure for capturing OpenGL Display events.
    {
    	Movement temp=movement;
    	DWORD time_elapsed;
    	// Process Application Loop
    	GLfloat light1_pos[4];
    	GLfloat light2_pos[4];
    	tickCount = GetTickCount ();				// Get The Tick Count
    	time_elapsed=tickCount-Game_env.lastTickCount;
    	Update (tickCount - Game_env.lastTickCount);	// Update The Counter
    	Game_env.lastTickCount = tickCount;

    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

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

    		gluPerspective(fovy, (GLfloat)W/(GLfloat)H, NEAR_Z, FAR_Z);
    							// This sets the view volume to be a Perspective one.
    	}
    	else if(Game_env.projection == ORTHOGRAPHIC)
    	{

    		glOrtho(-fViewVol, fViewVol, -fViewVol, fViewVol, NEAR_Z, FAR_Z);
    							// This sets the view volume to be a Orthographic one.
    	}
    	glMatrixMode(GL_MODELVIEW);
    	glColor3f(0.0, 0.0, 0.0);

    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();

    	if (targetCount==0)
    	{
    		Game_env.camera=THIRD_PERSON;
    		initializeMovement();
    		//movement.pitch=movement.roll=movement.yaw=0;
    		//movement.x=0;
    		//movement.z=2;
    		//movement.y=HecLevel;

    	}

    	//glLightfv(GL_LIGHT2 ,GL_SPOT_DIRECTION, lightthr_direction);
    	//glLightfv(GL_LIGHT2, GL_POSITION, lightthr_position);

    	//IN POLAR COORDINATES
    	if(Game_env.camera == THIRD_PERSON)
    	{
    		glutSetCursor(GLUT_CURSOR_NONE);
    		GLfloat fEye_x = radius * sin(theta*PI/180.0) * sin(phi*PI/180.0)+movement.x;
    		GLfloat fEye_y = radius * cos(theta*PI/180.0)+movement.y;
    		GLfloat fEye_z = radius * sin(theta*PI/180.0) * cos(phi*PI/180.0)+movement.z;

    		//gluLookAt(fEye_x, fEye_y, fEye_z, movement.x, movement.y, 1.0, 0, 1, 0);
    		gluLookAt(fEye_x, fEye_y, fEye_z, movement.x, movement.y, movement.z, 0, 1, 0);
    		// Specify the same camera position and orientation using polar coordiantes.

    		//glPushMatrix();

    			//drawNumber(time_elapsed);
    			//drawNumber(12345);
    			//glTranslatef(fEye_x, fEye_y, fEye_z);
    	}
    	else if (Game_env.camera == FIRST_PERSON)
    	{
            //glutSetCursor(GLUT_CURSOR_FULL_CROSSHAIR );

    		glutSetCursor(GLUT_CURSOR_CYCLE);

    		//First person view

    		GLfloat fLook_x=movement.x+
    			sin(deg2rad(movement.yaw))*cos(deg2rad(movement.pitch));
    		GLfloat fLook_y=movement.y+
    			sin(deg2rad(movement.pitch))*cos(deg2rad(movement.roll));
    		GLfloat fLook_z=movement.z+
    			cos(deg2rad(movement.yaw))*cos(deg2rad(movement.pitch));

    		GLfloat fUp_x = sin(deg2rad(movement.roll));
    		GLfloat fUp_y = cos(deg2rad(movement.roll));
    		GLfloat fUp_z = 0;

    		gluLookAt(movement.x, movement.y, movement.z, fLook_x, fLook_y, fLook_z,
    			fUp_x, fUp_y, fUp_z);

    		//drawNumber(time_elapsed);
    		//drawScoreBoard();
    		//drawNumber(12345);
    	}
    	glPushMatrix();
    		showReferenceAxis();
    		drawEnvironment();
    		//drawBuilding();
    		drawRockets(tickCount);

    	////////////////////////////////////////////////////////////////////////////////////////////
    		//target must be drawn AFTER rocket because explosion might happen
    		drawTargets(time_elapsed);
    		drawExplosion(tickCount);

    		glPushMatrix();
    			glTranslatef(movement.x, movement.y, movement.z);
    			glRotatef(movement.yaw , 0.0, 1.0, 0.0);
    			glRotatef(-movement.pitch , 1.0, 0.0, 0.0);
    			glRotatef(movement.roll , 0.0, 0.0, 1.0);
    			//glTranslatef(movement.x, movement.y, movement.z);

    			drawChopper();
    		glPopMatrix();
    		if (targetCount==0)
    		{
    			glTranslatef(0, 0, 2);
    			drawAnnounce(7);
    		}
    	glPopMatrix();
    	/************* NEW *****************/
    	// Lights Positioning:

    	//light 1:
    	light1_pos[0]=lightone_position[0]+movement.x;
    	light1_pos[1]=lightone_position[1]+movement.y;
    	light1_pos[2]=lightone_position[2]+movement.z;
    	light1_pos[3]=1;

    	light2_pos[0]=lighttwo_position[0]+movement.x;
    	light2_pos[1]=lighttwo_position[1]+movement.y;
    	light2_pos[2]=lighttwo_position[2]+movement.z;
    	light2_pos[3]=1;

    	//the head light is heading down a little bit
    	temp.pitch+=LightOnePitchOffset;
    	calcCoord(1, temp, lightone_direction[0], lightone_direction[1], lightone_direction[2]);
    	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightone_direction);
    	glLightfv(GL_LIGHT0, GL_POSITION, light1_pos);

    	//light 2:
    	//glPushMatrix();
    		//glRotatef(light2_circular, 0.0,1.0, 0.0);
    	glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, lighttwo_direction);
    	glLightfv(GL_LIGHT1, GL_POSITION, light2_pos);
    	//glPopMatrix();
    	/***********************************/

    	/********************* New *************/

    	if(!bSpin && !IsStart)
        {
    	   drawAnnounce(4);
    	}
    	glutSwapBuffers();
    }

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

    	glutInit(&argc, argv);
    	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    	glutInitWindowSize(SIZE, SIZE);
    	glutInitWindowPosition(300, 200);
    	glutCreateWindow("Chopper 2005."); //create the window with title

    	// Ask The User If They Want To Start In FullScreen Mode?
       if (MessageBox (HWND_DESKTOP, "Would You Like To Run In Fullscreen Mode?", "Start FullScreen?", MB_YESNO | MB_ICONQUESTION) == IDYES)
       {
    		glutFullScreen  ( ); 	// If yes, Run In fullScreen Mode
       }

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

    	/* Callback registrations with the OpenGL env are done here */
    	glutDisplayFunc(displayCallbackProc);  //call dispaly function call
    	glutKeyboardFunc(keyboardCallbackProc); // call keyboard event handler function
    	glutSpecialFunc(specialCallbackProc); //call keyboard special keys event handler
    	glutReshapeFunc(reShapeCallbackProc);  // call reshape image function
    	glutIdleFunc(Motion);//call idle handle function
    	glutMouseFunc( Mouse );//call mouse event

    	/* Callback registrations done.*/

    	glutMainLoop(); // loop until kill request
    	return 1;
    }

    void loadTargets()
    {
    	int i;
    	for (i=0; i<TargetFileCount; i++)
    	{
    		targetModelLists[i]=glmReadOBJ(targetFileNameLists[i]);
    		glmUnitize(targetModelLists[i]);					//Translate the model to origin and scale to fit in a unit cube.
    		glmFacetNormals(targetModelLists[i]);				//Generate face normals.
    		glmVertexNormals(targetModelLists[i], 90.0);
    	}
    	targetCount=MaxTargetCount;
    	for (i=0; i<MaxTargetCount; i++)
    	{
    		targets[i].coordIndex=i;
    		//the first group are tanks
    		if (i<6)
    		{
    			targets[i].modelIndex=0;
    		}
    		else
    		{
    			//these are plane
    			targets[i].modelIndex=1;
    		}
    	}
    }

    void drawTargets(DWORD time_elapsed)
    {
    	glEnable(GL_TEXTURE_2D);
    		//glDisable(GL_LIGHTING);
    	glBindTexture(GL_TEXTURE_2D, g_Texture[6]);

    	GLfloat x, y, z;
    	int coord_index, model_index;
    	for (int i=0; i<targetCount; i++)
    	{
    		glPushMatrix();
    		coord_index=targets[i].coordIndex;
    		model_index=targets[i].modelIndex;
    		//update target position so they are moving.
    		//update first
    		if (targetCoordLists[coord_index][2]>-FlyBoundary)
    		{
    			targetCoordLists[coord_index][2]-=TargetMovementSpeed*time_elapsed;
    		}
    		else
    		{
    			targetCoordLists[coord_index][2]+=FlyBoundary-10;
    		}

    		x=targetCoordLists[coord_index][0];
    		y=targetCoordLists[coord_index][1];
    		z=targetCoordLists[coord_index][2];
    		glTranslatef(x,y,z);
    		glRotatef(180, 0,1,0);
    		glmDraw(targetModelLists[model_index], GLM_SMOOTH | GLM_TEXTURE);

    		glPopMatrix();
    	}

    	glDisable(GL_TEXTURE_2D);
    }

    void openFire(DWORD now, int x, int y)
    {
    	float ratio, h_angle, v_angle;
    	if (rocketCount<MaxRocketCount)
    	{
    		v_angle=fovy/2.0;
    		h_angle=v_angle*(float)W/(float)H;

    		rockets[rocketCount].fireMoment=now;

    		//rockets[rocketCount].firePos=movement;
    		rockets[rocketCount].firePos.x=movement.x;
    		rockets[rocketCount].firePos.y =movement.y;
    		rockets[rocketCount].firePos.z=movement.z;
    		rockets[rocketCount].firePos.pitch=movement.pitch;
    		rockets[rocketCount].firePos.yaw=movement.yaw;
    		rockets[rocketCount].firePos.roll=movement.roll;

    		//the real ratio is not linear, but it doesn't matter
    		ratio=(float)(H/2.0-y)/(H/2.0);
    		rockets[rocketCount].firePos.pitch+= ratio*v_angle;
    		ratio=(float)(W/2.0-x)/(W/2.0);

    		rockets[rocketCount].firePos.yaw+=ratio*h_angle;

    		rocketCount++;
    	}
    }

    void drawRockets(DWORD now)
    {
    	float x, y, z;

    	glPushMatrix();

    	for (int i=0; i<rocketCount; i++)
    	{
    		if (calcRocket(i, now, x, y, z))
    		{
    			//glLoadIdentity();
    			glPushMatrix();
    			//glTranslatef(-movement.x, -movement.y, -movement.z);
    			glTranslatef(rockets[i].firePos.x, rockets[i].firePos.y, rockets[i].firePos.z);
    			//glTranslatef((movement.x+rockets[i].firePos.x)/2.0,
    			//	(FLOOR_LEVEL+rockets[i].firePos.y)/2.0, (movement.z+rockets[i].firePos.z)/2.0);

    			glTranslatef(x, y, z);
    			drawRocket();
    			glPopMatrix();
    		}
    		//check if there is explosion happened
    		if (explosionIndex!=-1)
    		{
    			//draw explosion at the coord of target and then remove target
    			//the explosionIndex is only the index of a list of target
    			createExplosion(targetCoordLists[targets[explosionIndex].coordIndex], now);
    			targetCount--;
    			for (int j=explosionIndex; j<targetCount; j++)
    			{
    				targets[j]=targets[j+1];
    			}
    			explosionIndex=-1;
    		}

    	}

    	//glLoadIdentity();
    	//glTranslatef(-movement.x, -movement.y, -movement.z);

    	//glTranslatef(0, FLOOR_LEVEL+1, 12);
    	//drawRocket();

    	glPopMatrix();
    }

    void drawRocket()
    {
    	glPushAttrib(GL_ALL_ATTRIB_BITS);
    	glPushMatrix();
    	glScalef(0.1, 0.1, 0.1);
    	//glTranslatef(movement.x, movement.y, movement.z);
    	glRotatef(movement.yaw , 0.0, 1.0, 0.0);
    	glRotatef(-movement.pitch , 1.0, 0.0, 0.0);
    	glRotatef(movement.roll , 0.0, 0.0, 1.0);
    	glColor3f(1,0,0);
    	glmDraw(missileModelLists[0], GLM_SMOOTH | GLM_TEXTURE);
    	//glColor3f(1,0,0);
    	//glutSolidSphere(RocketSize, 20, 20);
    	//glutSolidSphere(0.1, 20, 20);
    	glPopMatrix();
    	glPopAttrib();
    }

    bool boundaryCheck(float fx, float fy, float fz, const Movement& mv)
    {
    	float safeHeight;
    	/*
    	if (!(abs(fx)<FlyBoundary&&abs(fz)<FlyBoundary
    				&&fy>MinLatitude&&fy<MaxLatitude))
    	{
    		return false;
    	}
    	safeHeight=ChopperLength*cos(deg2rad(mv.roll))*sin(deg2rad(mv.pitch));
    	//pitch>0 means head up tail down and we must not let chopper tail touch ground
    	if (mv.pitch>0&&fy<safeHeight+MinLatitude)
    	{
    		return false;
    	}
    	return true;
    	*/

    	if (!(abs(mv.x+fx)<FlyBoundary&&abs(mv.z+fz)<FlyBoundary
    				&&mv.y+fy>MinLatitude&&mv.y+fy<MaxLatitude))
    	{
    		return false;
    	}
    	safeHeight=ChopperLength*cos(deg2rad(mv.roll))*sin(deg2rad(mv.pitch));
    	//pitch>0 means head up tail down and we must not let chopper tail touch ground
    	if (mv.pitch>0&&mv.y+fy<safeHeight+MinLatitude)
    	{
    		return false;
    	}
    	return true;

    }

    void createExplosion(float coord[3], DWORD now)
    {
    	glPushMatrix();
    	glTranslatef(-movement.x, -movement.y, -movement.z);
    	glTranslatef(coord[0], coord[1], coord[2]);
    	//explosionCount=rand()%(MaxExplosionPieces-1)+1;//at least one
    	explosionCount=MaxFragmentCount;//at least one
    	for (int i=0; i<explosionCount; i++)
    	{
    		explosions[i].firePos.x=coord[0];
    		explosions[i].firePos.y=coord[1];
    		explosions[i].firePos.z=coord[2];
    		explosions[i].firePos.pitch=rand()%90*(rand()%2==0?(-1):1);
    		explosions[i].firePos.yaw=rand()%360;
    		explosions[i].firePos.roll=rand()%90*(rand()%2==0?(-1):1);
    		explosions[i].fireMoment=now;
    	}
    }

    void drawExplosion(DWORD now)
    {
    	float distance, interval;
    	float x, y, z;
    	if (explosionCount>0)
    	{
    		interval=now-explosions[0].fireMoment;

    		if (interval>MaxFragmentFlyTime)
    		{
    			explosionCount=0;
    			return;
    		}
    		distance=interval*FragmentFlySpeed;
    		for (int i=0; i<explosionCount; i++)
    		{
    			calcCoord(distance, explosions[i].firePos, x, y, z);
    			glPushAttrib(GL_ALL_ATTRIB_BITS);
    			glPushMatrix();
    			glTranslatef(explosions[i].firePos.x,
    				explosions[i].firePos.y, explosions[i].firePos.z);
    			glTranslatef(x, y, z);
    			drawExplosionPiece(i);
    			glPopMatrix();
    			glPopAttrib();
    		}
    	}
    }

    void drawExplosionPiece(int index)
    {
    	float r,g,b, size;
    	r=rand()%11*0.1;
    	g=rand()%11*0.1;
    	b=rand()%11*0.1;
    	size=rand()%10*0.1;
    	glColor3f(r,g,b);
    	glutSolidCube(size);
    	//glmDraw(fragmentModelLists[index], GLM_SMOOTH | GLM_TEXTURE);
    }
    /*
    void loadFragments()
    {
    	char buf[256];
    	strcpy(buf, "data/frag_0.obj");
    	for (int i=0; i<MaxFragmentCount; i++)
    	{
    		buf[10]=i+'1';
    		fragmentModelLists[i]=glmReadOBJ(buf);
    		glmUnitize(fragmentModelLists[i]);					//Translate the model to origin and scale to fit in a unit cube.
    		glmFacetNormals(fragmentModelLists[i]);				//Generate face normals.
    		glmVertexNormals(fragmentModelLists[i], 90.0);
    	}
    }
    */

    /*
    bool loadNumbers()
    {
    	char buf[256];
    	strcpy(buf, "data/0.bmp");
    	for (int i=0; i<10; i++)
    	{
    		buf[5]=i+'0';
    		if (!CreateTexture(buf, g_Texture, i+PictureTextureStartIndex))
    		{
    			return false;
    		}
    	}
    	return true;
    }
    */

    void loadMissile()
    {
    	for (int i=0; i<MaxMissileModelCount; i++)
    	{
    		missileModelLists[i]=glmReadOBJ("data/missile.obj");
    		glmUnitize(missileModelLists[i]);					//Translate the model to origin and scale to fit in a unit cube.
    		glmFacetNormals(missileModelLists[i]);				//Generate face normals.
    		glmVertexNormals(missileModelLists[i], 90.0);
    	}
    }
    /*
    void drawBuilding()
    {
    	glPushAttrib(GL_ALL_ATTRIB_BITS);
    	glPushMatrix();
    	glEnable(GL_TEXTURE_2D);
    		//glDisable(GL_LIGHTING);
    	glBindTexture(GL_TEXTURE_2D, g_Texture[1]);

    	//glScalef(0.1, 0.1, 0.1);
    	for (int i=0; i<MaxSingleBuildingCount; i++)
    	{
    		glTranslatef(singleBuildingCoordLists[i][0], singleBuildingCoordLists[i][1],
    			singleBuildingCoordLists[i][2]);
    		glScalef(5,5,5);

    		glmDraw(singleBuildingModel, GLM_SMOOTH | GLM_TEXTURE);
    	}
    	//glColor3f(1,0,0);
    	//glutSolidSphere(RocketSize, 20, 20);
    	//glutSolidSphere(0.1, 20, 20);
    	glDisable(GL_TEXTURE_2D);

    	glPopMatrix();
    	glPopAttrib();

    	/*
    	glPushAttrib(GL_ALL_ATTRIB_BITS);
    	glPushMatrix();
    	//glScalef(0.1, 0.1, 0.1);
    	glTranslatef(manyBuildingCoord[0], manyBuildingCoord[1], manyBuildingCoord[2]);

    	glmDraw(manyBuildingModel, GLM_SMOOTH | GLM_TEXTURE);
    	//glColor3f(1,0,0);
    	//glutSolidSphere(RocketSize, 20, 20);
    	//glutSolidSphere(0.1, 20, 20);
    	glPopMatrix();
    	glPopAttrib();

    }
    */
    /*
    void loadBuilding()
    {
    	singleBuildingModel=glmReadOBJ(singleBuildingFileName);
    	glmUnitize(singleBuildingModel);					//Translate the model to origin and scale to fit in a unit cube.
    	glmFacetNormals(singleBuildingModel);				//Generate face normals.
    	glmVertexNormals(singleBuildingModel, 90.0);

    	for (int i=0; i<MaxSingleBuildingCount; i++)
    	{
    		singleBuildingCoordLists[i][0]=rand()%100*0.5*(rand()%2==0?1:(-1));
    		singleBuildingCoordLists[i][1]=FLOOR_LEVEL;
    		singleBuildingCoordLists[i][0]=rand()%100*0.5*(rand()%2==0?1:(-1));
    	}

    	manyBuildingModel=glmReadOBJ(manyBuildingFileName);
    	glmUnitize(manyBuildingModel);					//Translate the model to origin and scale to fit in a unit cube.
    	glmFacetNormals(manyBuildingModel);				//Generate face normals.
    	glmVertexNormals(manyBuildingModel, 90.0);

    }
    */
    /*

    void drawNumber(int number)
    {
    	int mod, coff=number;
    	int pos=0;
    	do
    	{
    		mod=coff%10;
    		drawDigit(mod, pos, 0);
    		pos++;
    		coff=coff/10;
    	}
    	while (coff!=0);
    }
    */
    /*
    void drawBoard(int pos)
    {
    	const float ScoreBoardPitchOffset=20;
    	const float ScoreBoardYawOffset=-20;
    	const float ScoreBoardPositionOffset=2;
    	const float ScoreBoardCubeSize=0.2;
    	Movement old=movement, temp;
    	float x,y,z;

    	old.pitch+=ScoreBoardPitchOffset;
    	old.yaw+=ScoreBoardYawOffset*pos;
    	//calcCoord(ScoreBoardPositionOffset, old, x,y,z);
    ///old.x+=x;
    //	old.y+=y;
    //	old.z+=z;

    	temp=old;
    	temp.pitch-=90;
    	temp.yaw+=45;

    	calcCoord(ScoreBoardCubeSize, temp, x,y,z);
    	glTexCoord2f(1.0,0.0);
    	glVertex3f(x+temp.x, y+temp.y, z+temp.z);

    	glTexCoord2f(0.0,0.0);

    	temp=old;
    	temp.pitch-=90;
    	temp.yaw-=45;
    	calcCoord(ScoreBoardCubeSize, temp, x,y,z);
    	glVertex3f(x+temp.x, y+temp.y, z+temp.z);

    	glTexCoord2f(0.0,1.0);

    	temp=old;
    	temp.pitch+=90;
    	temp.yaw-=45;
    	calcCoord(ScoreBoardCubeSize, temp, x,y,z);
    	glVertex3f(x+temp.x, y+temp.y, z+temp.z);

    	glTexCoord2f(1.0,1.0);

    	temp=old;
    	temp.pitch+=90;
    	temp.yaw-=45;
    	calcCoord(ScoreBoardCubeSize, temp, x,y,z);
    	glVertex3f(x+temp.x, y+temp.y, z+temp.z);

    }

    */
    /*

    void drawDigit(int digit, int pos, int start)
    {
    	const float ScoreBoardSphereRadius=0.2;
    	const float ScoreBoardPitchOffset=20;
    	const float ScoreBoardYawOffset=-20;
    	const float ScoreBoardPositionOffset=2;
    	Movement temp=movement;
    	float x,y,z;

    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, g_Texture[digit+PictureTextureStartIndex]);

    	glPushMatrix();
    	glPushAttrib(GL_ALL_ATTRIB_BITS);
    		glColor4f(1.0, 1.0, 1.0, 1.0);

    		temp.pitch+=ScoreBoardPitchOffset;
    		temp.yaw+=ScoreBoardYawOffset*pos;
    		calcCoord(ScoreBoardPositionOffset, temp, x,y,z);
    		glTranslatef(x,y,z);
    		glTranslatef(temp.x, temp.y, temp.z);
    		glRotatef(temp.yaw , 0.0, 1.0, 0.0);
    		glRotatef(-temp.pitch , 1.0, 0.0, 0.0);
    		glRotatef(temp.roll , 0.0, 0.0, 1.0);

    		GLUquadricObj *pObj = gluNewQuadric();
    							// Creates a new quadrics object and returns a pointer to it.
    		gluQuadricNormals(pObj,GLU_SMOOTH);
    								// specify one normal per vertex.
    		gluQuadricOrientation(pObj, GLU_OUTSIDE);
    								// For the quadrics object pObj,orientation is either GLU_OUTSIDE (the default)
    								// or GLU_INSIDE, which controls the direction in which normals are pointing.
    		gluQuadricTexture(pObj, GLU_TRUE);
    								// This turns on texture coordinates for our Quadric.
    		gluQuadricDrawStyle(pObj, GLU_FILL);

    		gluCube(pObj, ScoreBoardSphereRadius, 50, 50);
    								// Draw the sphere with a radius : fRadius.

    		gluDeleteQuadric(pObj);
    		//drawBoard(pos);
    		//glColor3f(1,0,0);
    		//glutSolidCube(ScoreBoardCubeSize);
    	glPopAttrib();
    	glPopMatrix();
    	glDisable(GL_TEXTURE_2D);
    }
    */
    /*
    void drawDigit(int digit, int pos, int start)
    {
    	const float ScoreBoardSphereRadius=0.2;
    	const float ScoreBoardPitchOffset=20;
    	const float ScoreBoardYawOffset=-20;
    	const float ScoreBoardPositionOffset=2;
    	Movement temp=movement;
    	float x,y,z;

    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, g_Texture[digit+PictureTextureStartIndex]);

    	glPushMatrix();
    	glPushAttrib(GL_ALL_ATTRIB_BITS);
    		glColor4f(1.0, 1.0, 1.0, 1.0);

    		drawBoard(pos);
    		//glColor3f(1,0,0);
    		//glutSolidCube(ScoreBoardCubeSize);
    	glPopAttrib();
    	glPopMatrix();
    	glDisable(GL_TEXTURE_2D);
    }

    void drawScoreBoard()
    {
    	//drawNumber(targetCount);
    	//drawDigit(1, 0, 0);

    }
    */

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

    ¡¡

    ¡¡

    ¡¡