flightsim.c

来自「飞机飞行界面」· C语言 代码 · 共 1,162 行 · 第 1/3 页

C
1,162
字号
/*
 * Flight simulator example from Chapter 16.
 *
 * Written by Michael Sweet
 */

#include <GL/glut.h>
#include <stdio.h>
#include <math.h>
#ifndef WIN32
#  include <sys/time.h>
#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;
	}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?