www.pudn.com > flightsim.rar > flightsim.c


/* 
 * Flight simulator example from Chapter 16. 
 * 
 * Written by Michael Sweet 
 */ 
 
#include  
#include  
#include  
#ifndef WIN32 
#  include  
#endif /* !WIN32 */ 
#ifndef M_PI 
#  define M_PI	(double)3.14159265358979323846 
#endif /* !M_PI */ 
#include "texture.h" 
#include "font.h" 
 
 
/* 
 * Constants: 
 */ 
 
#define TERRAIN_COUNT   41     /* Number of postings (must be odd) */ 
#define TERRAIN_SIZE    4000.0 /* Size of terrain database */ 
#define TERRAIN_VIEW    1000.0 /* Viewable distance in terrain database */ 
#define TERRAIN_SPACING 100.0  /* Spacing between postings */ 
 
 
/* 
 * Terrain posting structure... 
 */ 
 
typedef struct 
    { 
    GLfloat v[3]; /* Position */ 
    GLfloat n[3]; /* Lighting normal */ 
    } TP; 
 
 
/* 
 * Globals... 
 */ 
 
int        RedrawAll;            /* 1 = redraw everything, 0 = just terrain */ 
int        RedrawAirspeed;       /* 1 = redraw the airspeed indicator */ 
int        RedrawAltimeter;      /* 1 = redraw the altimeter */ 
int        RedrawCompass;        /* 1 = redraw the compass */ 
int        RedrawHorizon;        /* 1 = redraw the artificial horizon */ 
 
int        Width;                /* Width of window */ 
int        Height;               /* Height of window */ 
double     LastTime;             /* Last update time */ 
int	   MouseStartX;          /* Initial mouse X position */ 
int        MouseStartY;          /* Initial mouse Y position */ 
int	   MouseX;               /* Mouse X position */ 
int        MouseY;               /* Mouse Y position */ 
int        ViewAngle = 0;        /* Viewing angle */ 
GLenum     PolyMode = GL_FILL;   /* Polygon drawing mode */ 
int        UseTexturing = 1;     /* Use texturing? */ 
int        ShowLighting = 1;     /* Show lighting? */ 
int        ShowSky = 1;          /* Show sky? */ 
int        ShowTerrain = 1;      /* Show 3D terrain? */ 
int        ShowWater = 1;        /* Show water? */ 
GLfloat    Velocity = 10.0;      /* Flying speed */ 
GLfloat	   Position[3] =         /* Position of viewer */ 
    { 
    0.0, 10.0, 0.0 
    }; 
GLfloat	   Orientation[3] =      /* Orientation of viewer */ 
    { 
    0.0, 0.0, 0.0 
    }; 
TP         Terrain[TERRAIN_COUNT][TERRAIN_COUNT]; 
                                 /* Terrain postings */ 
GLuint     LandTexture;          /* Land texture object */ 
GLuint     SkyTexture;           /* Sky texture object */ 
 
GLFONT     *TextFont;            /* Font to use for text */ 
 
int        FPS = 0,              /* Frames per second value */ 
           FPSCount = 0;         /* Frames per second count */ 
double     FPSTime = 0.0;        /* Frames per second time */ 
GLfloat    Airspeed = 10.0;      /* Airspeed indicator */ 
GLfloat    Altimeter = 0.0;      /* Altimeter reading */ 
GLfloat    Horizon[2] =          /* Artificial horizon position */ 
    { 
    0.0, 0.0 
    }; 
GLfloat    Compass = 0.0;        /* Compass position */ 
 
int        UseSwapHint;          /* Use swap hint extension? */ 
PROC       glAddSwapHintRectWIN; /* Swap hint extension function */ 
 
/* 
 * Functions... 
 */ 
 
void   BuildTerrain(void); 
void   DivideTerrain(int left, int right, int bottom, int top); 
double GetClock(void); 
void   Idle(void); 
void   Joystick(unsigned state, int x, int y, int z); 
void   Keyboard(unsigned char key, int x, int y); 
void   Motion(int x, int y); 
void   Mouse(int button, int state, int x, int y); 
void   Redraw(void); 
void   Resize(int width, int height); 
void   Special(int key, int x, int y); 
 
 
/* 
 * 'main()' - Open a window and display a sphere and cube. 
 */ 
 
int                /* O - Exit status */ 
main(int  argc,    /* I - Number of command-line arguments */ 
     char *argv[]) /* I - Command-line arguments */ 
    { 
    glutInit(&argc, argv); 
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); 
    glutInitWindowSize(792, 573); 
    glutCreateWindow("Flight Simulator"); 
 
    glutDisplayFunc(Redraw); 
    if (glutDeviceGet(GLUT_HAS_JOYSTICK)) 
        glutJoystickFunc(Joystick, 200); 
    glutKeyboardFunc(Keyboard); 
    glutMotionFunc(Motion); 
    glutMouseFunc(Mouse); 
    glutReshapeFunc(Resize); 
    glutSpecialFunc(Special); 
 
    TextFont = FontCreate(wglGetCurrentDC(), "Arial", 16, 0, 0); 
 
    LandTexture = TextureLoad("land.bmp", GL_FALSE, GL_LINEAR_MIPMAP_LINEAR, 
                              GL_LINEAR, GL_REPEAT); 
    SkyTexture  = TextureLoad("sky.bmp", GL_FALSE, GL_LINEAR, GL_LINEAR, 
                              GL_CLAMP); 
 
    BuildTerrain(); 
 
    puts("QUICK HELP:"); 
    puts(""); 
    puts("ESC - Quit"); 
    puts("',' - Slow down, '<' - Slowest"); 
    puts("'.' - Speed up, '>' - Fastest"); 
    puts("'3' - Toggle terrain"); 
    puts("'l' - Toggle lighting"); 
    puts("'s' - Toggle sky/clouds"); 
    puts("'t' - Toggle texturing"); 
    puts("'w' - Toggle water"); 
    puts("'W' - Toggle wireframe"); 
 
    printf("GL_EXTENSIONS = %s\n", glGetString(GL_EXTENSIONS)); 
 
    if (strstr(glGetString(GL_EXTENSIONS), "WIN_swap_hint") != NULL) 
        { 
        UseSwapHint = 1; 
	glAddSwapHintRectWIN = wglGetProcAddress("glAddSwapHintRectWIN"); 
	} 
    else 
        UseSwapHint = 0; 
 
    RedrawAll = 1; 
 
    glutMainLoop(); 
    return (0); 
    } 
 
 
/* 
 * 'BuildTerrain()' - Build a fractal landscape... 
 */ 
 
void 
BuildTerrain(void) 
    { 
    int     i, j;           /* Looping vars */ 
    TP      *tp;            /* Terrain posting */ 
    GLfloat nx, ny, nz, nw; /* Normal components */ 
 
    for (i = 0, tp = Terrain[0]; i < TERRAIN_COUNT; i ++) 
        for (j = 0; j < TERRAIN_COUNT; j ++, tp ++) 
	    { 
	    tp->v[0] = TERRAIN_SPACING * i - 0.5 * TERRAIN_SIZE; 
	    tp->v[1] = 2.0; 
	    tp->v[2] = 0.5 * TERRAIN_SIZE - TERRAIN_SPACING * j; 
	    } 
 
    DivideTerrain(0, TERRAIN_COUNT - 1, 0, TERRAIN_COUNT - 1); 
 
    /* 
     * Loop through the terrain arrays and regenerate the 
     * lighting normals based on the terrain height. 
     */ 
    for (i = 0, tp = Terrain[0]; i < (TERRAIN_COUNT - 1); i ++, tp ++) 
        for (j = 0; j < (TERRAIN_COUNT - 1); j ++, tp ++) 
            { 
            /* 
             * Compute the cross product of the vectors above and to 
             * the right (simplified for this special case). 
             */ 
 
            nx = tp[0].v[1] - tp[1].v[1]; 
            ny = -1.0; 
            nz = tp[0].v[1] - tp[TERRAIN_COUNT].v[1]; 
            nw = -1.0 / sqrt(nx * nx + ny * ny + nz * nz); 
 
            /* Normalize the normal vector and store it... */ 
            tp->n[0] = nx * nw; 
            tp->n[1] = ny * nw; 
            tp->n[2] = nz * nw; 
            } 
 
    /* 
     * Set the top and right normals to be the same as the 
     * second-to-last normals. 
     */ 
 
    for (i = 0, tp = Terrain[TERRAIN_COUNT - 2]; 
         i < TERRAIN_COUNT; 
	 i ++, tp ++) 
        { 
	  tp[TERRAIN_COUNT].n[0] = tp[0].n[0]; 
	  tp[TERRAIN_COUNT].n[1] = tp[0].n[1]; 
	  tp[TERRAIN_COUNT].n[2] = tp[0].n[2]; 
	} 
 
    for (i = 0, tp = Terrain[0] + TERRAIN_COUNT - 2; 
         i < (TERRAIN_COUNT - 1); 
	 i ++, tp += TERRAIN_COUNT) 
        { 
	  tp[1].n[0] = tp[0].n[0]; 
	  tp[1].n[1] = tp[0].n[1]; 
	  tp[1].n[2] = tp[0].n[2]; 
	} 
 
    /* Position the viewer just above the terrain */ 
    Position[1] = Terrain[TERRAIN_COUNT / 2][TERRAIN_COUNT / 2].v[1] + 10.0; 
    } 
 
 
/* 
 * 'DivideTerrain()' - Subdivide terrain until all of the landscape is done. 
 */ 
 
void 
DivideTerrain(int left,   /* I - Left posting */ 
              int right,  /* I - Right posting */ 
	      int bottom, /* I - Bottom posting */ 
	      int top)    /* I - Top posting */ 
    { 
    int x, y;             /* Middle column and row */ 
    int halfel, maxel;    /* Maximum amount of change for middle */ 
 
    x      = (left + right) / 2; 
    y      = (bottom + top) / 2; 
    halfel = (rand() % 50) + 50; 
    maxel  = 2 * halfel + 1; 
 
    Terrain[x][bottom].v[1] = 0.5 * (Terrain[left][bottom].v[1] + 
                                     Terrain[right][bottom].v[1]) + 
                              ((rand() % maxel) - halfel) * 0.2; 
 
    Terrain[x][top].v[1]    = 0.5 * (Terrain[left][top].v[1] + 
                                     Terrain[right][top].v[1]) + 
                              ((rand() % maxel) - halfel) * 0.2; 
 
    Terrain[left][y].v[1]   = 0.5 * (Terrain[left][bottom].v[1] + 
                                     Terrain[left][top].v[1]) + 
                              ((rand() % maxel) - halfel) * 0.2; 
 
    Terrain[right][y].v[1]  = 0.5 * (Terrain[right][bottom].v[1] + 
                                     Terrain[right][top].v[1]) + 
                              ((rand() % maxel) - halfel) * 0.2; 
 
    if (x == (TERRAIN_COUNT / 2) && y == (TERRAIN_COUNT / 2)) 
        Terrain[x][y].v[1] = 0.25 * (Terrain[left][bottom].v[1] + 
                                     Terrain[right][bottom].v[1] + 
	    			     Terrain[left][top].v[1] + 
                                     Terrain[right][top].v[1]) + 
                             ((rand() % maxel) - halfel) * 0.5; 
    else 
        Terrain[x][y].v[1] = 0.25 * (Terrain[left][bottom].v[1] + 
                                     Terrain[right][bottom].v[1] + 
	    			     Terrain[left][top].v[1] + 
                                     Terrain[right][top].v[1]) + 
                             ((rand() % maxel) - halfel) * 0.2; 
 
    if ((x - left) > 1) 
        { 
	DivideTerrain(left, x, bottom, y); 
	DivideTerrain(left, x, y, top); 
	DivideTerrain(x, right, bottom, y); 
	DivideTerrain(x, right, y, top); 
	} 
    } 
 
 
/* 
 * 'GetClock()' - Return an increasing clock time in seconds... 
 */ 
 
double /* O - Time in seconds */ 
GetClock(void) 
    { 
#ifdef WIN32 
    SYSTEMTIME t;      /* Current time of day */ 
 
    GetSystemTime(&t); 
    return (((t.wHour * 60.0) + t.wMinute) * 60 + t.wSecond + 
            t.wMilliseconds * 0.001); 
#else /* UNIX */ 
    struct timeval t; /* Current time of day */ 
 
    gettimeofday(&t, NULL); 
    return ((double)t.tv_sec + 0.000001 * (double)t.tv_usec); 
#endif /* WIN32 */ 
    } 
 
 
/* 
 * 'Idle()' - Move the viewer and redraw... 
 */ 
 
void 
Idle(void) 
    { 
    int     i, j;         /* Column and row in terrain */ 
    GLfloat movex, movey; /* Scaled mouse movement */ 
    double  curtime;      /* Current time in milliseconds */ 
    GLfloat distance;     /* Distance to move */ 
    GLfloat cheading;     /* Cosine of heading */ 
    GLfloat sheading;     /* Sine of heading */ 
    GLfloat cpitch;       /* Cosine of pitch */ 
    GLfloat spitch;       /* Sine of pitch */ 
 
    /* Get the current system time to figure out how far to move. */ 
    curtime  = GetClock(); 
    distance = curtime - LastTime; 
    LastTime = curtime; 
 
    /* 
     * See how far the mouse pointer is from the 'center' (click) 
     * position. 
     */ 
 
    movex = 0.01 * sqrt(Velocity) * (MouseX - MouseStartX); 
    movey = 0.01 * sqrt(Velocity) * (MouseY - MouseStartY); 
 
    /* 
     * Adjust roll, pitch, and heading according to the current 
     * mouse inputs and orientation. 
     */ 
 
    Orientation[0] += distance * movey * cos(Orientation[2] * M_PI / 180.0); 
    Orientation[1] += distance * movey * sin(Orientation[2] * M_PI / 180.0); 
    Orientation[2] += distance * movex; 
 
    /* Move based upon the current orientation... */ 
    cheading = cos(Orientation[1] * M_PI / 180.0); 
    sheading = sin(Orientation[1] * M_PI / 180.0); 
    cpitch   = cos(Orientation[0] * M_PI / 180.0); 
    spitch   = sin(Orientation[0] * M_PI / 180.0); 
 
    Position[0] += Velocity * distance * sheading * cpitch; 
    Position[1] += Velocity * distance * spitch; 
    Position[2] -= Velocity * distance * cheading * cpitch; 
 
    /* Limit the viewer to the terrain... */ 
    if (Position[0] < (-0.45 * TERRAIN_SIZE)) 
        { 
	Position[0]    = -0.45 * TERRAIN_SIZE; 
	Orientation[1] += 10.0 * distance; 
	} 
 
    if (Position[0] > (0.45 * TERRAIN_SIZE)) 
        { 
	Position[0]    = 0.45 * TERRAIN_SIZE; 
	Orientation[1] += 10.0 * distance; 
	} 
 
    if (Position[2] < (-0.45 * TERRAIN_SIZE)) 
        { 
	Position[2]    = -0.45 * TERRAIN_SIZE; 
	Orientation[1] += 10.0 * distance; 
	} 
 
    if (Position[2] > (0.45 * TERRAIN_SIZE)) 
        { 
	Position[2]    = 0.45 * TERRAIN_SIZE; 
	Orientation[1] += 10.0 * distance; 
	} 
 
    i = (Position[0] + 0.5 * TERRAIN_SIZE) / TERRAIN_SPACING + 0.5; 
    j = (0.5 * TERRAIN_SIZE - Position[2]) / TERRAIN_SPACING + 0.5; 
 
    if (Position[1] < (5.0 + Terrain[i][j].v[1])) 
        { 
	Position[1]    = 5.0 + Terrain[i][j].v[1]; 
	Orientation[0] += 5.0 * distance; 
	} 
 
    if (Position[1] > (0.2 * TERRAIN_SIZE)) 
        { 
	Position[1]    = 0.2 * TERRAIN_SIZE; 
	Orientation[0] -= 5.0 * distance; 
	} 
 
    if (Orientation[0] < -90.0) 
        { 
        Orientation[0] = -180.0 - Orientation[0]; 
	Orientation[2] += 180.0; 
	} 
    else if (Orientation[0] > 90.0) 
        { 
        Orientation[0] = 180.0 - Orientation[0]; 
	Orientation[2] -= 180.0; 
	} 
 
    if (Orientation[1] < 0.0) 
        Orientation[1] += 360.0; 
    else if (Orientation[1] > 360.0) 
        Orientation[1] -= 360.0; 
 
    if (Orientation[2] < -180.0) 
        Orientation[2] += 360.0; 
    else if (Orientation[2] > 180.0) 
        Orientation[2] -= 360.0; 
 
    /* Update instruments as needed... */ 
    if (fabs(Orientation[0] - Horizon[0]) > 1.0 || 
        fabs(Orientation[2] - Horizon[1]) > 1.0) 
        RedrawHorizon = 1; 
 
    if (fabs(Orientation[1] - Compass) > 1.0 && 
        fabs(Orientation[1] - Compass) < 359.0) 
	RedrawCompass = 1; 
 
    if (Position[1] != Altimeter) 
        RedrawAltimeter = 1; 
 
    /* Update the frames-per-second value */ 
    FPSTime += distance; 
    FPSCount ++; 
    if (FPSCount >= 20) 
        { 
	FPS       = FPSCount / FPSTime + 0.5; 
	FPSTime   = 0.0; 
	FPSCount  = 0; 
	} 
 
    RedrawAll = -1; 
    Redraw(); 
    } 
 
 
/* 
 * 'Joystick()' - Handle joystick movement. 
 */ 
 
void 
Joystick(unsigned state,       /* I - Button state */ 
         int      x,           /* I - X position (-1000 to 1000) */ 
 	 int      y,           /* I - Y position (-1000 to 1000) */ 
	 int      z)           /* I - Z position (-1000 to 1000) */ 
    { 
    float      new_velocity;   /* New velocity value */ 
    static int last_state = 0; /* Last button state */ 
 
    if (last_state != state) 
        { 
	/* Button changed state; see what the new state is... */ 
	if (state && !last_state) 
            { 
    	    /* Start flying */ 
            MouseStartX = MouseX = x / 2; 
	    MouseStartY = MouseY = y / 2; 
	    LastTime    = GetClock(); 
            glutIdleFunc(Idle); 
	    } 
            else if (!state && last_state) 
            { 
	    /* Stop flying */ 
            glutIdleFunc((void (*)(void))0); 
	    } 
 
	last_state = state; 
	} 
 
    /* Update the joystick/mouse position */ 
    if (state) 
        { 
	MouseX = x / 2; 
        MouseY = y / 2; 
	} 
 
    if (z > -999) 
        Velocity = (999 - z) * 0.045 + 10.0; 
 
    if (fabs(Velocity - Airspeed) >= 1.0) 
	RedrawAirspeed = 1; 
    } 
 
/* 
 * 'Keyboard()' - Handle key presses... 
 */ 
 
void 
Keyboard(unsigned char key, /* I - Key that was pressed */ 
         int           x,   /* I - Mouse X position */ 
	 int           y)   /* I - Mouse Y position */ 
    { 
    switch (key) 
        { 
	case 0x1b : 
	    puts(""); 
	    exit(0); 
	    break; 
	case ',' : 
	    if (Velocity > 5.0) 
	        { 
		RedrawAirspeed = 1; 
	        Velocity       -= 5.0; 
		} 
	    break; 
	case '.' : 
	    if (Velocity < 100.0) 
	        { 
		RedrawAirspeed = 1; 
	        Velocity       += 5.0; 
		} 
	    break; 
	case '<' : 
	    RedrawAirspeed = 1; 
	    Velocity       = 10.0; 
	    break; 
	case '>' : 
	    RedrawAirspeed = 1; 
	    Velocity       = 100.0; 
	    break; 
	case '3' : 
	    ShowTerrain = !ShowTerrain; 
	    break; 
	case 'l' : 
	    ShowLighting = !ShowLighting; 
	    break; 
	case 's' : 
	    ShowSky = !ShowSky; 
	    break; 
	case 't' : 
	    UseTexturing = !UseTexturing; 
	    break; 
	case 'w' : 
	    ShowWater = !ShowWater; 
	    break; 
	case 'W' : 
	    if (PolyMode == GL_FILL) 
	        PolyMode = GL_LINE; 
	    else 
	        PolyMode = GL_FILL; 
	    break; 
	} 
 
    glutPostRedisplay(); 
    } 
 
 
/* 
 * 'Motion()' - Handle mouse pointer motion. 
 */ 
 
void 
Motion(int x, /* I - Current mouse X position */ 
       int y) /* I - Current mouse Y position */ 
    { 
    MouseX = x; 
    MouseY = y; 
    } 
 
 
/* 
 * 'Mouse()' - Handle mouse button events. 
 */ 
 
void 
Mouse(int button, /* I - Button that changed */ 
      int state,  /* I - Current button states */ 
      int x,      /* I - Current mouse X position */ 
      int y)      /* I - Current mouse Y position */ 
    { 
    if (state == GLUT_DOWN) 
        { 
	/* Start flying */ 
	MouseStartX = MouseX = x; 
	MouseStartY = MouseY = y; 
	LastTime    = GetClock(); 
        glutIdleFunc(Idle); 
	} 
    else 
        { 
	/* Stop flying */ 
        glutIdleFunc((void (*)(void))0); 
	} 
    } 
 
 
/* 
 * 'draw_instrument()' - Draws an instrument frame. 
 */ 
 
void 
draw_instrument(int ix, int iy, int isize) 
    { 
    float	theta; /* Angle for circle */ 
 
    glColor3f(0.5, 0.5, 0.525); 
    glBegin(GL_QUADS); 
    glVertex2i(ix, iy); 
    glVertex2i(ix + isize - 1, iy); 
    glVertex2i(ix + isize - 1, iy + isize - 1); 
    glVertex2i(ix, iy + isize - 1); 
    glEnd(); 
 
    glColor3f(0.75, 0.75, 0.788); 
    glBegin(GL_LINE_STRIP); 
    glVertex2i(ix, iy); 
    glVertex2i(ix, iy + isize - 1); 
    glVertex2i(ix + isize - 1, iy + isize - 1); 
    glEnd(); 
 
    glColor3f(0.25, 0.25, 0.262); 
    glBegin(GL_LINE_STRIP); 
    glVertex2i(ix, iy); 
    glVertex2i(ix + isize - 1, iy); 
    glVertex2i(ix + isize - 1, iy + isize - 1); 
    glEnd(); 
 
    glColor3f(1.0, 1.0, 1.0); 
    glBegin(GL_TRIANGLE_FAN); 
    glVertex2f(ix + isize * 0.5f, iy + isize * 0.5f); 
 
    for (theta = 0.0f; theta < (2.0f * M_PI + M_PI / 18.0f); theta += M_PI / 18.0f) 
	glVertex2f(ix + isize * 0.5f + cos(theta) * isize * 0.4f, 
	           iy + isize * 0.5f + sin(theta) * isize * 0.4f); 
    glEnd(); 
    } 
 
 
/* 
 * 'Redraw()' - Redraw the window... 
 */ 
 
void 
Redraw(void) 
    { 
    int   i, j; 
    int   ix, iy, isize; 
    TP    *tp; 
    float theta, st, ct; 
    static GLfloat sunpos[4] = { -0.7071, -0.7071, 0.0, 0.0 }; 
    static GLfloat suncolor[4] = { 1.0, 1.0, 0.8, 1.0 }; 
    static GLfloat sunambient[4] = { 0.25, 0.25, 0.2, 1.0 }; 
    static GLfloat s_vector[4] = { 50.0 / TERRAIN_SIZE, 0.0, 0.0, 0.0 }; 
    static GLfloat t_vector[4] = { 0.0, 0.0, 50.0 / TERRAIN_SIZE, 0.0 }; 
 
    /* Force a full redraw for normal situations; Idle sets it to -1 */ 
    RedrawAll ++; 
 
    /* Reset the viewport... */ 
    glViewport(0, 3 * Height / 8, Width, Height - 3 * Height / 8); 
    glScissor(0, 3 * Height / 8, Width, Height - 3 * Height / 8); 
    glEnable(GL_SCISSOR_TEST); 
 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    gluPerspective(22.5, 2.0f * (float)Width / (float)Height, 4.0, 
                   TERRAIN_VIEW); 
    glMatrixMode(GL_MODELVIEW); 
 
    /* Clear the window to light blue... */ 
    if (ShowSky && UseTexturing && PolyMode == GL_FILL) 
        glClear(GL_DEPTH_BUFFER_BIT); 
    else 
        { 
        glClearColor(0.75, 0.75, 1.0, 1.0); 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
	} 
 
    /* Setup viewing transformations for the current orientation... */ 
    glPushMatrix(); 
    glRotatef((float)ViewAngle, 0.0, 1.0, 0.0); 
    glRotatef(Orientation[2], 0.0, 0.0, 1.0); 
    glRotatef(Orientation[0], -1.0, 0.0, 0.0); 
    glRotatef(Orientation[1], 0.0, 1.0, 0.0); 
    glTranslatef(0.0, -Position[1], 0.0); 
 
    glPolygonMode(GL_FRONT_AND_BACK, PolyMode); 
 
    /* Draw the sky */ 
    if (UseTexturing && SkyTexture && ShowSky && PolyMode == GL_FILL) 
        { 
        glEnable(GL_TEXTURE_2D); 
	glBindTexture(GL_TEXTURE_2D, SkyTexture); 
 
        glColor3f(1.0, 1.0, 1.0); 
        glBegin(GL_TRIANGLE_FAN); 
        glTexCoord2f(0.5, 0.5); 
        glVertex3f(0.0, TERRAIN_VIEW, 0.0); 
        for (theta = 0.0; theta < (2.1 * M_PI); theta += M_PI / 8) 
            { 
            ct = cos(theta); 
            st = sin(theta); 
            glTexCoord2f(0.5 + 0.3 * ct, 0.5 + 0.3 * st); 
            glVertex3f(ct * TERRAIN_VIEW * 0.7071, 
	               TERRAIN_VIEW * 0.7071, 
	               st * TERRAIN_VIEW * 0.7071); 
            } 
        glEnd(); 
 
        glBegin(GL_TRIANGLE_STRIP); 
        for (theta = 0.0; theta < (2.1 * M_PI); theta += M_PI / 8) 
            { 
            ct = cos(theta); 
            st = sin(theta); 
            glTexCoord2f(0.5 + 0.3 * ct, 0.5 + 0.3 * st); 
            glVertex3f(ct * TERRAIN_VIEW * 0.7071, 
	               TERRAIN_VIEW * 0.7071, 
	               st * TERRAIN_VIEW * 0.7071); 
            glTexCoord2f(0.5 + 0.5 * ct, 0.5 + 0.5 * st); 
            glVertex3f(ct * TERRAIN_VIEW, -100.0, 
	               st * TERRAIN_VIEW); 
            } 
        glEnd(); 
        } 
 
    /* Setup lighting if needed... */ 
    glEnable(GL_LIGHTING); 
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); 
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, sunambient); 
    glEnable(GL_COLOR_MATERIAL); 
 
    if (ShowLighting) 
        { 
        glEnable(GL_LIGHT0); 
        glLightfv(GL_LIGHT0, GL_POSITION, sunpos); 
        glLightfv(GL_LIGHT0, GL_DIFFUSE, suncolor); 
        glLightfv(GL_LIGHT0, GL_AMBIENT, sunambient); 
        } 
    else 
        glDisable(GL_LIGHT0); 
 
    /* Then draw the terrain... */ 
    if (UseTexturing && LandTexture && PolyMode == GL_FILL) 
        { 
        glEnable(GL_TEXTURE_2D); 
	glBindTexture(GL_TEXTURE_2D, LandTexture); 
        } 
 
    glTranslatef(-Position[0], 0.0, -Position[2]); 
 
    glColor3f(0.3, 0.8, 0.2); 
 
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); 
    glTexGenfv(GL_S, GL_OBJECT_PLANE, s_vector); 
 
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); 
    glTexGenfv(GL_T, GL_OBJECT_PLANE, t_vector); 
 
    glEnable(GL_TEXTURE_GEN_S); 
    glEnable(GL_TEXTURE_GEN_T); 
 
    if (ShowTerrain) 
        { 
        glEnable(GL_CULL_FACE); 
        glEnable(GL_DEPTH_TEST); 
        glDepthFunc(GL_LEQUAL); 
 
        for (i = 0, tp = Terrain[0]; i < (TERRAIN_COUNT - 1); i ++) 
            { 
            glBegin(GL_TRIANGLE_STRIP); 
            for (j = 0; j < TERRAIN_COUNT; j ++, tp ++) 
                { 
                glNormal3fv(tp[0].n); 
	        glVertex3fv(tp[0].v); 
 
                glNormal3fv(tp[TERRAIN_COUNT].n); 
	        glVertex3fv(tp[TERRAIN_COUNT].v); 
	        } 
            glEnd(); 
	    } 
        glDisable(GL_CULL_FACE); 
        glDisable(GL_DEPTH_TEST); 
        } 
     else 
        { 
        glBegin(GL_QUADS); 
        glNormal3f(0.0, 1.0, 0.0); 
        glVertex3f(-0.5 * TERRAIN_SIZE, 0.0, -0.5 * TERRAIN_SIZE); 
        glVertex3f( 0.5 * TERRAIN_SIZE, 0.0, -0.5 * TERRAIN_SIZE); 
        glVertex3f( 0.5 * TERRAIN_SIZE, 0.0,  0.5 * TERRAIN_SIZE); 
        glVertex3f(-0.5 * TERRAIN_SIZE, 0.0,  0.5 * TERRAIN_SIZE); 
        glEnd(); 
	} 
 
    glDisable(GL_TEXTURE_GEN_S); 
    glDisable(GL_TEXTURE_GEN_T); 
 
    if (ShowWater && ShowTerrain) 
        { 
        glEnable(GL_DEPTH_TEST); 
 
        glColor4f(0.0, 0.0, 0.25, 0.75); 
        for (i = 0; i < (TERRAIN_COUNT - 1); i ++) 
	    { 
	    glBegin(GL_TRIANGLE_STRIP); 
            glNormal3f(0.0, 1.0, 0.0); 
            for (j = 0; j < TERRAIN_COUNT; j ++) 
	        { 
		glTexCoord2i(i, j); 
		glVertex3f(i * TERRAIN_SPACING - 0.5 * TERRAIN_SIZE, 0.0, 
		           0.5 * TERRAIN_SIZE - j * TERRAIN_SPACING); 
 
		glTexCoord2i(i + 1, j); 
		glVertex3f((i + 1) * TERRAIN_SPACING - 0.5 * TERRAIN_SIZE, 0.0, 
		           0.5 * TERRAIN_SIZE - j * TERRAIN_SPACING); 
		} 
	    glEnd(); 
	    } 
 
        glDisable(GL_DEPTH_TEST); 
        } 
 
    glPopMatrix(); 
    glDisable(GL_TEXTURE_2D); 
    glDisable(GL_LIGHTING); 
 
    /* Figure out the sizes of the controls... */ 
    isize = Height / 4; 
    if ((19 * isize / 4) > Width) 
	isize = Width / 5; 
 
    ix = (Width - 19 * isize / 4) / 2; 
    iy = (Height / 2 - 3 * isize / 2) / 2; 
 
    /* Redraw the FPS and instruments... */ 
    glViewport(0, 0, Width, Height); 
    glDisable(GL_SCISSOR_TEST); 
 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glOrtho(0.0f, (float)Width, 0.0f, (float)Height, -1.0, 1.0); 
    glMatrixMode(GL_MODELVIEW); 
 
    /* Show the cockpit outline */ 
    glColor3f(0.2f, 0.2f, 0.21f); 
    glBegin(GL_TRIANGLE_FAN); 
    glVertex2i(Width / 2, 3 * Height / 8); 
    for (theta = 0.0; theta < (M_PI + M_PI / 20.0); theta += M_PI / 20.0) 
        glVertex2f(Width / 2 - Width / 2 * cos(theta), 
	           3 * Height / 8 + Height / 8 * sin(theta)); 
    glEnd(); 
 
    /* Show the frames-per-second in the upper window... */ 
    glColor3f(1.0f, 1.0f, 1.0f); 
    glRasterPos2i(Width / 2, 7 * Height / 16); 
    FontPrintf(TextFont, 0, "FPS = %d", FPS); 
 
    if (RedrawAll) 
        { 
        glColor3f(0.2f, 0.2f, 0.21f); 
	glBegin(GL_QUADS); 
	glVertex2i(0, 0); 
	glVertex2i(Width, 0); 
	glVertex2i(Width, 3 * Height / 8); 
	glVertex2i(0, 3 * Height / 8); 
	glEnd(); 
        } 
 
    if (RedrawAirspeed || RedrawAll) 
        { 
	/* 
	 * Redraw the airspeed indicator... 
	 */ 
 
        Airspeed = Velocity; 
 
        draw_instrument(ix, iy, isize); 
 
        glColor3f(0.0f, 0.0f, 0.0f); 
	glRasterPos2i(ix + isize * 0.5f, iy + isize * 0.3f); 
	FontPrintf(TextFont, 0, "KNOTS"); 
 
	glBegin(GL_LINES); 
        for (i = 0; i < 200; i += 10) 
	    { 
	    theta = i * M_PI * 2.0f / 200.0f; 
	    st    = sin(theta); 
	    ct    = cos(theta); 
 
	    glVertex2f(ix + isize * 0.5 + st * 0.3f * isize, 
	               iy + isize * 0.5f + ct * 0.3f * isize); 
	    glVertex2f(ix + isize * 0.5 + st * 0.4f * isize, 
	               iy + isize * 0.5f + ct * 0.4f * isize); 
	    } 
	glEnd(); 
 
        glRasterPos2f(ix + isize * 0.5f, iy + isize * 0.2f + 2); 
	FontPrintf(TextFont, 0, "100"); 
 
        glRasterPos2f(ix + isize * 0.2f, iy + isize * 0.5f - 4); 
	FontPrintf(TextFont, 1, "150"); 
 
        glRasterPos2f(ix + isize * 0.5f, iy + isize * 0.8f - 10); 
	FontPrintf(TextFont, 0, "0"); 
 
        glRasterPos2f(ix + isize * 0.8f, iy + isize * 0.5f - 4); 
	FontPrintf(TextFont, -1, "50"); 
 
        glPushMatrix(); 
	    glTranslatef(ix + isize * 0.5f, iy + isize * 0.5f, 0.0); 
	    glRotatef((Airspeed * 3600.0f / 1852.0f) * 360.0f / 200.0f, 
	              0.0f, 0.0f, -1.0f); 
 
            glColor3f(1.0f, 0.0f, 0.0f); 
	    glBegin(GL_TRIANGLES); 
	    glVertex2f(-0.05f * isize, 0.0f); 
	    glVertex2f( 0.05f * isize, 0.0f); 
	    glVertex2f( 0.0f, 0.35f * isize); 
	    glEnd(); 
	glPopMatrix(); 
	} 
 
    ix += 5 * isize / 4; 
    iy += isize / 4; 
 
    if (RedrawAltimeter || RedrawAll) 
        { 
	/* 
	 * Redraw the altimeter... 
	 */ 
 
        Altimeter = Position[1]; 
 
        draw_instrument(ix, iy, isize); 
 
        glColor3f(0.0f, 0.0f, 0.0f); 
	glRasterPos2i(ix + isize * 0.5f, iy + isize * 0.5f); 
	glBitmap(0, 0, 0, 0, 0, -5, NULL); 
	FontPrintf(TextFont, 0, "%05d FT", (int)(Altimeter * 3.28)); 
 
	glBegin(GL_LINES); 
        for (i = 0; i < 100; i += 10) 
	    { 
	    theta = i * M_PI * 2.0f / 100.0f; 
	    st    = sin(theta); 
	    ct    = cos(theta); 
 
	    glVertex2f(ix + isize * 0.5 + st * 0.3f * isize, 
	               iy + isize * 0.5f + ct * 0.3f * isize); 
	    glVertex2f(ix + isize * 0.5 + st * 0.4f * isize, 
	               iy + isize * 0.5f + ct * 0.4f * isize); 
	    } 
	glEnd(); 
 
        glRasterPos2f(ix + isize * 0.5f, iy + isize * 0.8f - 10); 
	FontPrintf(TextFont, 0, "0"); 
 
        glPushMatrix(); 
	    glTranslatef(ix + isize * 0.5f, iy + isize * 0.5f, 0.0); 
	    glRotatef(fmod(Altimeter * 0.0328, 1.0) * 360.0f, 
	              0.0f, 0.0f, -1.0f); 
 
            glColor3f(1.0f, 0.0f, 0.0f); 
	    glBegin(GL_TRIANGLES); 
	    glVertex2f(-0.05f * isize, 0.0f); 
	    glVertex2f( 0.05f * isize, 0.0f); 
	    glVertex2f( 0.0f, 0.35f * isize); 
	    glEnd(); 
	glPopMatrix(); 
	} 
 
    ix += 5 * isize / 4; 
 
    if (RedrawHorizon || RedrawAll) 
        { 
	/* 
	 * Redraw the artificial horizon; we draw a 2D horizon which isn't 
	 * perfect but illustrates the point... 
	 */ 
 
	float start, end; /* Start and end angles of horizon */ 
 
        Horizon[0] = Orientation[0]; 
	Horizon[1] = Orientation[2]; 
 
        draw_instrument(ix, iy, isize); 
 
        start = M_PI * (90.0f + Horizon[0] - Horizon[1]) / 180.0f; 
	end   = M_PI * (270.0f - Horizon[0] - Horizon[1]) / 180.0f; 
 
        glColor3f(1.0f, 0.0f, 0.0f); 
	glBegin(GL_POLYGON); 
 
	for (theta = start; theta < end; theta += M_PI / 18) 
	    glVertex2f(ix + isize * 0.5f + sin(theta) * isize * 0.4f, 
	               iy + isize * 0.5f + cos(theta) * isize * 0.4f); 
 
	glVertex2f(ix + isize * 0.5f + sin(end) * isize * 0.4f, 
	           iy + isize * 0.5f + cos(end) * isize * 0.4f); 
	glEnd(); 
 
        glColor3f(0.0f, 0.0f, 0.0f); 
	glBegin(GL_LINES); 
	glVertex2f(ix + 0.2f * isize, iy + 0.5f * isize); 
	glVertex2f(ix + 0.4f * isize, iy + 0.5f * isize); 
	glVertex2f(ix + 0.6f * isize, iy + 0.5f * isize); 
	glVertex2f(ix + 0.8f * isize, iy + 0.5f * isize); 
	glEnd(); 
	} 
 
    ix += 5 * isize / 4; 
    iy -= isize / 4; 
 
    if (RedrawCompass || RedrawAll) 
        { 
	/* 
	 * Redraw the compass... 
	 */ 
 
        Compass = Orientation[1]; 
 
        draw_instrument(ix, iy, isize); 
 
        glPushMatrix(); 
	    glTranslatef(ix + isize * 0.5f, iy + isize * 0.5f, 0.0); 
	    glRotatef(Compass, 0.0f, 0.0f, 1.0f); 
 
            glColor3f(0.0f, 0.0f, 0.0f); 
 
            glRasterPos2f(0.0f, isize * 0.3f); 
	    glBitmap(0, 0, 0, 0, 0, -5, NULL); 
	    FontPrintf(TextFont, 0, "N"); 
 
            glRasterPos2f(0.0f, -isize * 0.3f); 
	    glBitmap(0, 0, 0, 0, 0, -5, NULL); 
	    FontPrintf(TextFont, 0, "S"); 
 
            glRasterPos2f(isize * 0.3f, 0.0f); 
	    glBitmap(0, 0, 0, 0, 0, -5, NULL); 
	    FontPrintf(TextFont, 0, "E"); 
 
            glRasterPos2f(-isize * 0.3f, 0.0f); 
	    glBitmap(0, 0, 0, 0, 0, -5, NULL); 
	    FontPrintf(TextFont, 0, "W"); 
 
            glBegin(GL_LINES); 
            for (i = 0; i < 360; i += 10) 
	        if (i % 90) 
		    { 
		    theta = i * M_PI / 180.0f; 
	            st    = sin(theta); 
	            ct    = cos(theta); 
 
	            glVertex2f(st * 0.3f * isize, ct * 0.3f * isize); 
	            glVertex2f(st * 0.4f * isize, ct * 0.4f * isize); 
		    } 
	    glEnd(); 
 
	    glRotatef(Compass, 0.0f, 0.0f, -1.0f); 
 
            glColor3f(1.0f, 0.0f, 0.0f); 
	    glBegin(GL_TRIANGLES); 
	    glVertex2f(0.0f, 0.3f * isize); 
	    glVertex2f(-0.05f * isize, 0.1f * isize); 
	    glVertex2f(0.05f * isize, 0.1f * isize); 
	    glEnd(); 
 
            glRasterPos2i(0, 0); 
	    FontPrintf(TextFont, 0, "%03d", (int)Compass); 
 
	glPopMatrix(); 
	} 
 
    ix -= 15 * isize / 4; 
 
    if (!RedrawAll && UseSwapHint) 
        { 
	/* Tell windows just to swap the top */ 
	(*glAddSwapHintRectWIN)(0, 3 * Height / 8, Width, 
	                        Height - 3 * Height / 8); 
 
        /* and any instruments we've changed... */ 
	if (RedrawAirspeed) 
	    (*glAddSwapHintRectWIN)(ix, iy, isize, isize); 
	if (RedrawAltimeter) 
	    (*glAddSwapHintRectWIN)(ix + 5 * isize / 4, iy + isize / 4, isize, isize); 
	if (RedrawHorizon) 
	    (*glAddSwapHintRectWIN)(ix + 10 * isize / 4, iy + isize / 4, isize, isize); 
	if (RedrawCompass) 
	    (*glAddSwapHintRectWIN)(ix + 15 * isize / 4, iy, isize, isize); 
	} 
 
    /* Finish up */ 
    glutSwapBuffers(); 
 
    RedrawAll       = 0; 
    RedrawAirspeed  = 0; 
    RedrawAltimeter = 0; 
    RedrawCompass   = 0; 
    RedrawHorizon   = 0; 
    } 
 
 
/* 
 * 'Resize()' - Resize the window... 
 */ 
 
void 
Resize(int width,  /* I - Width of window */ 
       int height) /* I - Height of window */ 
    { 
    /* Save the new width and height */ 
    Width  = width; 
    Height = height; 
 
    /* Force the whole window to be redrawn... */ 
    RedrawAll = 1; 
    } 
 
 
/* 
 * 'Special()' - Handle special keys like left, right, up, and down. 
 */ 
 
void 
Special(int key, /* I - Key that was pressed */ 
        int x,   /* I - X location of the mouse pointer */ 
	int y)   /* I - Y location of the mouse pointer */ 
    { 
    switch (key) 
        { 
	case GLUT_KEY_UP : 
	    ViewAngle = 0; 
	    glutPostRedisplay(); 
	    break; 
	case GLUT_KEY_DOWN : 
	    ViewAngle = 180; 
	    glutPostRedisplay(); 
	    break; 
	case GLUT_KEY_LEFT : 
	    ViewAngle = (ViewAngle + 315) % 360; 
	    glutPostRedisplay(); 
	    break; 
	case GLUT_KEY_RIGHT : 
	    ViewAngle = (ViewAngle + 45) % 360; 
	    glutPostRedisplay(); 
	    break; 
	} 
    }