
/*----------------------------------------------------------------------
 *                                    TIGA
 *          Copyright (C) 1989-1990  Texas Instruments Incorporated.
 *                            All Rights Reserved
 *----------------------------------------------------------------------
 *
 *       TIGA Example Application Program:  3D Flight Simulator
 *
 *  This file contains the core of an example TIGA-340 application
 *  program written in C.  The functions defined in this file execute on
 *  the host PC processor, and communicate with the GSP through the TIGA
 *  Communications Driver.
 *
 *  This program demonstrates the ability of the 34010 to do realtime 3D
 *  animation.  The flight of an aircraft through a three-dimensional
 *  scene is simulated.  The controls and instrument panel are simpler
 *  than those of flight simulators that try to accurately represent the
 *  instruments and aerodynamic behavior of a real airplane.
 *----------------------------------------------------------------------
 * History:
 *     04/06/89...Original version written...................J. Van Aken
 *     10/01/90...Added flight playback feature..............J. Van Aken
 *----------------------------------------------------------------------
 */
#define RLMNAME  "flyprims"  /* name of flight simulator RLM */
#define DBNAME   "flydata"   /* name of default 3D database file */

/*------------------ Microsoft C 5.1 include files -------------------*/
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

/*------------------------ TIGA include files ------------------------*/
#include <typedefs.h>
#include <tiga.h>

/*------------------ flight simulator include files ------------------*/
#include "flytypes.h"    /* flight simulator type definitions */
#include "flyprims.h"    /* define flysim extended functions */
#include "flyconst.h"    /* flight simulator constant definitions */
#include "scancode.h"    /* IBM PC/AT keyboard scan codes */

/*---------------------- messages to PC screen -----------------------*/
char *msg[] = {
    { "TIGA Sample Application--3D Flight Simulator, Version 2.0\n"      },
    { "Copyright (c) 1989-1990  Texas Instruments Incorporated.\n" },
    { "\n\n--- Command-Line Argument Usage ---\n\n" },
    { "     Select TIGA video mode:        -m<mode number>\n" },
    { "     Disable instrument panel:      -i0\n" },
    { "     Specify 3D viewport width:     -w<width value>\n" },
    { "     Specify 3D viewport height:    -h<height value>\n" },
    { "     Specify 3D database file:      -d<file name>\n" },
    { "     >>Default database file name is \"flydata.bin\"\n" },
    { "\n\n--- Flight Simulator Command Keys ---\n\n" },
    { "   'X' = accelerate       'Z' = decelerate   \n" },
    { "   '<' = bank left        '?' = bank right   \n" },
    { "   '>' = nose up          'L' = nose down    \n" },
    { "   'K' = kill speed       'P' = pause action \n" },
    { "   'M' = masher on/off    'I' = instruments on/off \n" },
    { "   'R' = reset game       'Q' = quit (exit to DOS) \n\n" },
    { "\7==>Score by successfully flying under \"masher\". \n" },
};

/*--------------------- aircraft flight controls ---------------------*/
#define BANK_RIGHT  SC_FSLASH    /* char = "?", key no. = 53 */
#define BANK_LEFT   SC_COMMA     /* char = "<", key no. = 51 */

#define NOSE_UP     SC_DOT       /* char = ">", key no. = 52 */
#define NOSE_DOWN   SC_L         /* char = "L", key no. = 38 */

#define SPEED_UP    SC_X         /* char = "X", key no. = 45 */
#define SLOW_DOWN   SC_Z         /* char = "Z", key no. = 44 */
#define AIR_BRAKE   SC_K         /* char = "K", key no. = 37 */

#define PAUSE       SC_P         /* char = "P", key no. = 25 */
#define RESET       SC_R         /* char = "R", key no. = 19 */

#define IPANEL      SC_I         /* char = "I", key no. = 23 */
#define MASHER      SC_M         /* char = "M", key no. = 50 */

#define ESCAPE      SC_ESC       /* ESCAPE key, key no. = 1  */
#define QUIT        SC_Q         /* char = "Q", key no. = 16 */

/*------------- constraints on aircraft flight behavior --------------*/
#define MINALT   (5*ZNEAR/4) /* aircraft's minimum possible altitude */
#define MAXSPEED (20<<8)     /* maximum possible airspeed in FIX8     */
#define ROTINC   (1<<4)      /* rotation increment = 1/8 deg in FIX8  */
#define MAXDYAW  (3<<8)      /* max. rate of yaw in FIX8 format       */
#define MAXDPCH  (3<<8)      /* max. rate of pitch in FIX8 format     */
#define MAXPCH   (90<<8)     /* max. absolute pitch in FIX8 format    */
#define MAXROL   (60<<8)     /* max. absolute roll in FIX8 format     */

/*------------------- flight simulator play modes --------------------*/
#define FREERUN      1      /* start free running demo (open cmd file) */
#define PLAYBACK     2      /* playback mode (read cmd file)           */
#define RECORD       3      /* record mode (output keys to cmd file)   */
#define JUSTFLY      4      /* just fly (get key cmds; don't record)   */

/*--------------------- miscellaneous constants ----------------------*/
#define ONE      0x40000000  /* constant 1.0 in FIX30 format          */
#define ZNEAR    8           /* distance from eye to z "hither" plane */

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;

extern double sqrt(), ldexp();

static long trigfun();         /* get sin and cos of angle t */
static long rotvec();          /* rotate 3 orthogonal unit vectors */

/*------- static parameters shared by functions in this module -------*/
static ulong db3dptr=NIL;      /* pointer to 3D database in GSP memory */
static CONFIG config;         /* TIGA configuration data */
static VIEWPOINT view;        /* aircraft orientation and position */
static VIEWPORT cpvp;         /* 3D viewport for cockpit window view */
static VIEWPORT ahvp;         /* 3D viewport for artificial horizon */
static DBASE3D header;        /* header info from 3D database file */
static short screenwide, screenhigh;  /* screen dimensions */
static long airspeed;         /* aircraft speed in FIX8 format */
static long yaw, pitch, roll; /* aircraft yaw, pitch, roll angles */
static VIEWINFO trigvals;     /* cos, sin of yaw, pitch, roll angles */
static long dyaw, dpitch;     /* pitch and yaw angular velocities */
static short inbx;            /* inside bounding box? */
static short score;           /* keep track of player's score */
static uchar keybuf[133];     /* scan codes for PC/AT keyboard */
static ushort keypress;       /* key press status */
static short vidmode=-1;      /* video mode (default = -1) */
static short ipenable=-1;     /* instrument panel enable */
static short vptwide=-1, vpthigh=-1;  /* 3D viewport dimensions */
static char *dbname;          /* database filename */
static short runmode;         /* flight simulator play mode */
static short oldmode;

/*----------------------------------------------------------------------
 *
 *                            main program
 *
 *----------------------------------------------------------------------
 */
main(argc,argv)
int  argc;
char *argv[];
{
    uchar *keybdon();      /* keyboard input initialization function */
    int status;            /* status of bounding box collision */
    int needs_help;        /* flag indicating user needs help */
    int i;
    char *p;

    keybdon();            /* install keyboard interrupt routine */
    for (i = 0; i <= 8; ++i)
        printf("%s", msg[i]);

    /* Process command line arguments. */
    runmode = JUSTFLY;    /* interactive keyboard input mode */
    needs_help = FALSE;
    dbname = DBNAME;
    for (i = 1; i < argc; i++) {
        p = argv[i];
        if (*p++ == '-') {
            switch (*p++) {
            case 'm':
            case 'M':
                if (isdigit(*p))
                    vidmode = atoi(p);      /* set TIGA video mode */
                else
                    needs_help = TRUE;
                break;
            case 'i':
            case 'I':
                if (isdigit(*p))
                    ipenable = atoi(p);     /* instruments on/off */
                else
                    needs_help = TRUE;
                break;
            case 'w':
            case 'W':
                if (isdigit(*p))
                    vptwide = atoi(p);      /* set width of viewport */
                else
                    needs_help = TRUE;
                break;
            case 'h':
            case 'H':
                if (isdigit(*p))
                    vpthigh = atoi(p);      /* set height of viewport */
                else
                    needs_help = TRUE;
                break;
            case 'd':
            case 'D':
                dbname = p;
                break;
            case 'f':
            case 'F':
                runmode = FREERUN;          /* free-running demo mode */
                break;
            default:
                needs_help = TRUE;
                break;
            }
        } else
            needs_help = TRUE;
        if (needs_help)
            quit_flysim();
    }

    /* Initialize flight simulator. */
    init_tiga();            /* initialize GSP-resident part of TIGA */
    load_dbase(dbname);     /* read 3D database from disk */
    init_flysim();          /* initialize flight simulator */
    for (i = 9; i <= 16; ++i)
        printf("%s", msg[i]);

    /* Loop below monitors flight controls and draws scene. */
    while (cmd_interpret()) {   /* input next command from user */
        view_params();     /* get sin, cos of yaw, pitch, roll */
        /* Check for collision with any object's bounding box, and
         * synchronize to GSP to prevent comm buffer overwrite.
         */
        if (status = collide(&view.xyz)) {
            if (inbx == 0) {       /* just now entered bounding box */
                inbx = 1;          /* remember we're inside box now */
                if (status - 1)    /* flew under bouncing cube (yay) */
                    ++score;       /* pilot gains a point */
                else {             /* mashed by bouncing cube (ouch) */
                    if (score)
                        --score;   /* pilot loses a point */
                    view.xyz.x -= (view.uvw.wx>>17) * airspeed >> 13;
                    view.xyz.y -= (view.uvw.wy>>17) * airspeed >> 13;
                    view.xyz.z -= (view.uvw.wz>>17) * airspeed >> 13;
                    airspeed = -airspeed / 2;
                }
            }
        } else             /* there was no collision */
            inbx = 0;      /* so remember we're NOT in a bounding box */

        /* Tell GSP to draw next frame (3D scene and instruments). */
        draw_frame(&view, &trigvals, airspeed, score);
    }
    quit_flysim();
}


/*----------------------------------------------------------------------
 *
 *              Quit flight simulator (exit to MS DOS).
 *
 *----------------------------------------------------------------------
 */
quit_flysim()
{
    keybdoff();
    if (db3dptr != NIL)
        gsp_free(db3dptr);
    set_videomode(oldmode,NO_INIT);
    tiga_set(CD_CLOSE);
    exit(0);
}


/*----------------------------------------------------------------------
 *
 *            initialization of GSP-resident part of TIGA
 *
 *----------------------------------------------------------------------
 */
init_tiga()
{
    int return_code;
    char *s;
    short v;

    /* Initialize TIGA interface. */
    if ((v = tiga_set(CD_OPEN)) < 0) {
        printf("TIGA CD error: %d\n", v);
        exit(0);
    }
    oldmode = get_videomode();
    if  (!set_videomode(TIGA, INIT | CLR_SCREEN)) {
        printf("\n==>ERROR - Unable to initialize TIGA.\n");
        printf("Aborting...\n");
        quit_flysim();
    }
    if (vidmode >= 0) {       /* command line argument */
        if (!set_config(vidmode, 1)) {
            printf("\n==>ERROR - TIGA mode number %d is not supported\n",
                    vidmode);
            quit_flysim();
        }
    }
    get_config(&config);
    set_config(config.current_mode, 1);

    screenwide = config.mode.disp_hres;
    screenhigh = config.mode.disp_vres;
    clear_screen(0);
    init_palet();

    /* Load application-specific TIGA extended primitives. */
    /* Write messages to both screens. */
    text_out(0, 0, "Loading flight simulator...");
    printf("\n==>Downloading %s extended functions to GSP...\n",
            RLMNAME);
    if ((return_code = install_rlm(RLMNAME)) < 0) {
        printf("\n==>ERROR - Unable to install %s extensions,",
                 RLMNAME);
        printf(" error code = %d\n", return_code);
        quit_flysim();
    }
    if (return_code != 0) {
        printf("\n==>ERROR - The module number returned");
        printf(" when %s installed is not 0.\n", RLMNAME);
        printf("Module number returned = %d", return_code);
        quit_flysim();
    }
}


/*----------------------------------------------------------------------
 *
 *                 initialization of flight simulator
 *
 *----------------------------------------------------------------------
 */
init_flysim()
{
    double x, y;

    /* Define dimensions of 3D viewport. */
    if (vptwide > screenwide || vptwide < 2)
        vptwide = screenwide;
    if (vpthigh > screenhigh || vpthigh < 2)
        vpthigh = screenhigh;
    if (ipenable != 0 && vpthigh > screenhigh-IPHT)
        vpthigh = screenhigh-IPHT;
    if (ipenable == 0 && vpthigh > screenhigh)
        vpthigh = screenhigh;

    /* Define 3D viewport parameters. */
    cpvp.sx = vptwide/2;
    cpvp.sy = vpthigh/2;
    cpvp.cx = screenwide/2;
    if (ipenable)
        cpvp.cy = (screenhigh-IPHT)/2;
    else
        cpvp.cy = screenhigh/2;
    cpvp.xleft = cpvp.cx - cpvp.sx;
    cpvp.ytop  = cpvp.cy - cpvp.sy;
    x = cpvp.sx;
    y = cpvp.sy;
    cpvp.rdiag = sqrt(x*x+y*y);
    cpvp.d = (vptwide + screenwide)/2;
    cpvp.znear = ZNEAR;

    /* Define 3D viewport for artificial horizon meter. */
    ahvp.xleft = screenwide/2 - (MSPACE+MDIAM)/2;
    ahvp.ytop  = screenhigh - (IPHT+MDIAM)/2;
    ahvp.sx = ahvp.sy = ahvp.d = MRADIUS;
    ahvp.cx = MDIAM/2 + ahvp.xleft;
    ahvp.cy = MDIAM/2 + ahvp.ytop;
    ahvp.znear = ZNEAR;
    ahvp.rdiag = MRADIUS;

    /* Initialize starting xyz coordinates in world coordinate space. */
    view.xyz = header.start;   /* get start xyz from database header */
    if (view.xyz.z < MINALT)
        view.xyz.z = MINALT;   /* minimum altitude */

    /* Initialize speed and orientation in world coordinate space. */
    yaw = pitch = roll = 0;   /* plane initially level, facing north */
    dyaw = dpitch = 0;        /* plane initially not spinning */
    airspeed = 0;             /* plane initially not moving */

    /* Initialize collision detection variables for bouncing cube. */
    inbx = 0;                 /* initially outside all bounding boxes */
    score = 0;                /* player's initial score */

    /* Initialize parameters on GSP side of flight simulator. */
    init_flygsp(&cpvp, &ahvp, db3dptr, ipenable);
}


/*----------------------------------------------------------------------
 *
 *        Download 3D database from binary data file on disk.
 *            Initialize variables "db3dptr" and "header".
 *
 *----------------------------------------------------------------------
 */
#define  NOSWZL     0            /* don't swizzle bytes */

load_dbase(fname)
char *fname;
{
    size_t fread();
    FILE *fin, *fopen();
    char *sin, bufin[BUFSIZ];  /* BUFSIZ defined in stdio.h */
    int c;
    int ext;
    long p;
    long int  length; 

    /*------ Open specified database file for input. */
    sin = bufin;
    ext = FALSE;
    while ((c = *fname) != '\0') {
        ext |= (c == '.');     /* find start of file name extension */
        *sin++ = *fname++;     /* copy next char of file name */
    }
    if (!ext)                  /* append default file name extension */
        strcpy(sin, ".bin");
    else
        strcpy(sin, "");
    if ((fin = fopen(bufin, "rb")) == (FILE *)NULL) {
        printf("\n==>ERROR - Unable to open database file %s.\n", bufin);
        quit_flysim();
    }

    /*------ Read 3D database header, and download it to GSP. */
    if (fread(&header, sizeof(DBASE3D), 1, fin) != 1) {
        printf("\n==>ERROR - Unable to read header from %s.\n", bufin);
        quit_flysim();
    }

    length = (long)(header.dbsize);  /* get size of database in bytes */

    
    if (!(db3dptr = (gsp_malloc(gsp_maxheap())))) {
        printf("\n==>ERROR - Unable to allocate GSP memory.\n");
        quit_flysim();
    }
    host2gsp(&header, ((long)db3dptr), sizeof(DBASE3D), NOSWZL);
    p = (long)(db3dptr + 8*sizeof(DBASE3D));
    /*------ Download remainder of 3D database to GSP. */
    while ((length = (long)fread(bufin, sizeof(char), BUFSIZ, fin)) > 0) {
        host2gsp(bufin, p, (ushort)length, NOSWZL);
        p += 8*length;
    }
}


/*----------------------------------------------------------------------
 *
 *                           keyboard input routines
 *
 *----------------------------------------------------------------------
 */
#pragma intrinsic (inp, outp)

#define KEYBD       9         /* keyboard interrupt number */

static void (far interrupt *oldkb)(void);
static void far interrupt newkb(void);

uchar *keybdon()
{
    int i;

    for (i = 0; i < sizeof(keybuf); i++)
        keybuf[i] = 0;

    oldkb = _dos_getvect(KEYBD);
    _dos_setvect(KEYBD,newkb);

    return (keybuf);
}


keybdoff()
{
    _dos_setvect(KEYBD,oldkb);
}


wait_keypress()
{
    for (keypress = FALSE; !keypress; )
        ;
}


static void far interrupt newkb()
{
    ushort scancode;

    /* Detect key press (status = 1) or release (status = 0). */
    scancode = inp(0x60);
    keypress = (scancode & 0x80) ? FALSE : TRUE;
    scancode &= 0x7F;
    keybuf[scancode] = keypress;

    /* signal end of interrupt to keyboard and controller */
    outp(0x61,inp(0x61) | 0x80);
    outp(0x61,inp(0x61) & 0x7F);
    outp(0x20,0x20);
}


/*----------------------------------------------------------------------
 *
 *                    keyboard command interpreter
 *
 *----------------------------------------------------------------------
 */
#define ESCAPE_BIT      (1<<12)
#define MASHER_BIT      (1<<11)
#define IPANEL_BIT      (1<<10)
#define PAUSE_BIT       (1<<9)
#define RESET_BIT       (1<<8)
#define QUIT_BIT        (1<<7)
#define NOSE_UP_BIT     (1<<6)
#define NOSE_DOWN_BIT   (1<<5)
#define BANK_RIGHT_BIT  (1<<4)
#define BANK_LEFT_BIT   (1<<3)
#define SPEED_UP_BIT    (1<<2)
#define SLOW_DOWN_BIT   (1<<1)
#define AIR_BRAKE_BIT   1


int cmd_interpret()
{
    size_t fread();
    fpos_t *pos;
    FILE *fopen();
    static FILE *fin;
    unsigned short cmd;
    unsigned short cmdbuf[32];

    cmd = 2*(2*(2*(2*(2*(2*(2*(2*(2*(2*(2*(2*keybuf[ESCAPE]
                                             + keybuf[MASHER])
                                           + keybuf[IPANEL])
                                        + keybuf[PAUSE])
                                     + keybuf[RESET])
                                  + keybuf[QUIT])
                               + keybuf[NOSE_UP])
                            + keybuf[NOSE_DOWN])
                         + keybuf[BANK_RIGHT])
                      + keybuf[BANK_LEFT])
                  + keybuf[SPEED_UP])
              + keybuf[SLOW_DOWN])
           + keybuf[AIR_BRAKE];
    if (runmode == FREERUN) {
        /*------ Open binary file "playback.bin" for input. */
        if ((fin = fopen("playback.bin", "rb+")) == (FILE *)NULL) {
            /* File does not exist.  Create it and begin recording. */
            if ((fin = fopen("playback.bin", "wb+")) == (FILE *)NULL) {
                printf("\n==>ERROR - Unable to create playback.bin file.\n");
                quit_flysim();
            }
            runmode = RECORD;
        } else
            /* File exists and has been opened for input. */
            runmode = PLAYBACK;
    }
    if (runmode == PLAYBACK) {
        if (cmd) {                          /* key has been pressed */
            if (cmd & QUIT_BIT) {           /* quit */
                printf("\7\n==>Bye!\n");
                while (keybuf[QUIT])
                    ;
                return (0);
            }
            if (cmd & ESCAPE_BIT) {         /* begin recording keys */
                runmode = RECORD;
                /*------ Switch from playback.bin file from input to append. */
                if (fgetpos(fin, pos) != 0) {
                    printf("\n==>ERROR - Unable to getpos in playback.bin.\n");
                    quit_flysim();
                }
                if (fsetpos(fin, pos) != 0) {
                    printf("\n==>ERROR - Unable to append to playback.bin.\n");
                    quit_flysim();
                }
            } else {
                runmode = JUSTFLY;          /* don't record keyboard inputs */
                /*------ Close playback.bin file. */
                if (fclose(fin) == EOF) {
                    printf("\n==>ERROR - Unable to close playback.bin file.\n");
                    quit_flysim();
                }
            }
        } else {
            /*------ Read next 16 bits from playback.bin file. */
            if (fread(&cmdbuf[15], sizeof(short), 1, fin) != 1) {
                printf("\n==>ERROR - Unable to read data from playback.bin.\n");
                quit_flysim();
            }
            cmd = cmdbuf[15];
            if (cmd & (QUIT_BIT | RESET_BIT))
                rewind(fin);
        }
    }
    if (runmode == RECORD) {
        /*------ Append next 16 bits to playback.bin file. */
        if (fwrite(&cmd, sizeof(short), 1, fin) != 1) {
            printf("\n==>ERROR - Unable to write data to playback.bin.\n");
            quit_flysim();
        }
        if (cmd & RESET_BIT) {
            runmode = PLAYBACK;
            /*----- Setup to read input binary file from beginning. */
            rewind(fin);
            if (fgetpos(fin, pos) != 0) {
                printf("\n==>ERROR - Unable to rewind playback.bin.\n");
                quit_flysim();
            }
            if (fsetpos(fin, pos) != 0) {
                printf("\n==>ERROR - Unable to setpos in playback.bin.\n");
                quit_flysim();
            }
            init_flysim();
            while (keybuf[RESET])
                ;
        }
    }

    /* Viewplane "right" initially aligned with world x axis */
    view.uvw.ux = ONE;
    view.uvw.uy = 0;
    view.uvw.uz = 0;

    /* View "down" vector initially aligned with world -z axis */
    view.uvw.vx = 0;
    view.uvw.vy = 0;
    view.uvw.vz = -ONE;

    /* Viewplane normal vector initially aligned with world y axis */
    view.uvw.wx = 0;
    view.uvw.wy = ONE;
    view.uvw.wz = 0;

    if (cmd & MASHER_BIT) {
        keybuf[MASHER] = FALSE;
        toggle_masher();
    }

    if ((cmd & IPANEL_BIT) != 0 && ipenable != 0) {
        keybuf[IPANEL] = FALSE;
        toggle_ipanel();
    }

    if (cmd & PAUSE_BIT)
        wait_keypress();

    if (cmd & RESET_BIT) {
        init_flysim();
        while (keybuf[RESET])
            ;
    }

    if (cmd & QUIT_BIT) {
        printf("\7\n==>Bye!\n");
        while (keybuf[QUIT])
            ;
        return (0);
    }

    if (cmd & NOSE_UP_BIT) {
        if ((dpitch += ROTINC) > MAXDPCH)
            dpitch = MAXDPCH;
    } else if (cmd & NOSE_DOWN_BIT) {
        if ((dpitch -= ROTINC) < -MAXDPCH)
            dpitch = -MAXDPCH;
    }

    pitch += dpitch;
    rotvec(VW, pitch>>6, &view.uvw);
    if (dpitch > 0) {
        if ((dpitch -= ROTINC/2) < 0)
            dpitch = 0;
    } else if (dpitch < 0) {
        if ((dpitch += ROTINC/2) > 0)
            dpitch = 0;
    }

    if (cmd & BANK_RIGHT_BIT) {
        if ((dyaw += ROTINC) > MAXDYAW)
            dyaw = MAXDYAW;
    } else if (cmd & BANK_LEFT_BIT) {
        if ((dyaw -= ROTINC) < -MAXDYAW)
            dyaw = -MAXDYAW;
    }
    yaw += dyaw;
    rotvec(XY, yaw>>6, &view.uvw);
    if (dyaw > 0) {
        if ((dyaw -= ROTINC/4) < 0)
            dyaw = 0;
    } else if (dyaw < 0) {
        if ((dyaw += ROTINC/4) > 0)
            dyaw = 0;
    }

    if ((roll = airspeed*dyaw>>14) > MAXROL>>6)
        roll = MAXROL>>6;
    else if (roll < -MAXROL>>6)
        roll = -MAXROL>>6;
    rotvec(UV, roll, &view.uvw);

    if (cmd & SPEED_UP_BIT) {
        if ((airspeed += (airspeed>>11) + 32) > MAXSPEED)
            airspeed = MAXSPEED;
    } else if (cmd & SLOW_DOWN_BIT) {
        if ((airspeed -= (airspeed>>11) + 32) < -MAXSPEED)
            airspeed = -MAXSPEED;
    } else if (cmd & AIR_BRAKE_BIT)
        airspeed = dyaw = dpitch = 0;

    view.xyz.x += (view.uvw.wx>>17) * airspeed >> 13;
    view.xyz.y += (view.uvw.wy>>17) * airspeed >> 13;
    if ((view.xyz.z += (view.uvw.wz>>17)*airspeed >> 13)
                                                          < MINALT<<8) {
        view.xyz.z = MINALT << 8;      /* scraping on the ground */
        if (abs((short)airspeed) < (1<<6))   /* is speed close to zero? */
            airspeed = 0;             /* make it EXACTLY zero */
        else
            airspeed -= airspeed >> 6;
    }

    return (1);
}


/*----------------------------------------------------------------------
 *
 *                get viewpoint orientation parameters
 *
 *----------------------------------------------------------------------
 */

view_params()
{
    double s, t;
    long r, u0, u1, u2, v2, w2;

    u0 = view.uvw.wx >> 15;
    u1 = view.uvw.wy >> 15;
    u2 = view.uvw.wz >> 15;
    v2 = view.uvw.uz >> 15;
    w2 = view.uvw.vz >> 15;

    s = u0;
    t = u1;
    r = sqrt(s * s + t * t);
    if (r > 128) {
        /* Format:  32-bit fixed-point with 30-bit fraction */
        trigvals.csyaw = (u0 << 15) / r << 15;    /*   cos(yaw)   */
        trigvals.snyaw = (u1 << 15) / r << 15;    /*   sin(yaw)   */
        trigvals.cspch = r              << 15;    /*   cos(pitch) */
        trigvals.snpch = u2             << 15;    /*   sin(pitch) */
    }
    s = w2;
    t = v2;
    r = sqrt(s * s + t * t);
    if (r > 128) {
        trigvals.csrol = (-w2 << 15) / r << 15;    /*   cos(roll)  */
        trigvals.snrol = (-v2 << 15) / r << 15;    /*   sin(roll)  */
    }
}


/*----------------------------------------------------------------------
 *  Sine lookup table for angles 0 to 90 degrees in 1/4-degree steps.
 *  Each sine value is expressed as a 32-bit fixed-point value with
 *  31 bits of fraction.
 *----------------------------------------------------------------------
 */
static long sintbl[] = {
    0x00000000, 0x008efa17, 0x011df37c, 0x01aceb7c,     /* 0 deg. */
    0x023be165, 0x02cad485, 0x0359c428, 0x03e8af9e,     /* 1 deg. */
    0x04779632, 0x05067734, 0x059551f1, 0x062425b6,     /* 2 deg. */
    0x06b2f1d2, 0x0741b592, 0x07d07044, 0x085f2137,     /* 3 deg. */
    0x08edc7b7, 0x097c6313, 0x0a0af299, 0x0a997598,     /* 4 deg. */
    0x0b27eb5c, 0x0bb65336, 0x0c44ac72, 0x0cd2f660,     /* 5 deg. */
    0x0d61304e, 0x0def598a, 0x0e7d7162, 0x0f0b7727,     /* 6 deg. */
    0x0f996a26, 0x102749af, 0x10b5150f, 0x1142cb98,     /* 7 deg. */
    0x11d06c97, 0x125df75b, 0x12eb6b35, 0x1378c774,     /* 8 deg. */
    0x14060b68, 0x1493365f, 0x152047ab, 0x15ad3e9a,     /* 9 deg. */
    0x163a1a7e, 0x16c6daa6, 0x17537e63, 0x17e00505,     /* 10 deg. */
    0x186c6ddd, 0x18f8b83c, 0x1984e373, 0x1a10eed3,     /* 11 deg. */
    0x1a9cd9ac, 0x1b28a351, 0x1bb44b14, 0x1c3fd045,     /* 12 deg. */
    0x1ccb3237, 0x1d56703c, 0x1de189a6, 0x1e6c7dc7,     /* 13 deg. */
    0x1ef74bf3, 0x1f81f37c, 0x200c73b4, 0x2096cbf1,     /* 14 deg. */
    0x2120fb83, 0x21ab01c0, 0x2234ddfb, 0x22be8f87,     /* 15 deg. */
    0x234815ba, 0x23d16fe8, 0x245a9d65, 0x24e39d85,     /* 16 deg. */
    0x256c6f9f, 0x25f51307, 0x267d8713, 0x2705cb19,     /* 17 deg. */
    0x278dde6e, 0x2815c06a, 0x289d7061, 0x2924edac,     /* 18 deg. */
    0x29ac37a0, 0x2a334d96, 0x2aba2ee4, 0x2b40dae2,     /* 19 deg. */
    0x2bc750e9, 0x2c4d9050, 0x2cd39871, 0x2d5968a3,     /* 20 deg. */
    0x2ddf0040, 0x2e645ea1, 0x2ee9831f, 0x2f6e6d16,     /* 21 deg. */
    0x2ff31bde, 0x30778ed2, 0x30fbc54d, 0x317fbeab,     /* 22 deg. */
    0x32037a45, 0x3286f779, 0x330a35a1, 0x338d341b,     /* 23 deg. */
    0x340ff242, 0x34926f74, 0x3514ab0e, 0x3596a46c,     /* 24 deg. */
    0x36185aee, 0x3699cdf2, 0x371afcd5, 0x379be6f6,     /* 25 deg. */
    0x381c8bb5, 0x389cea72, 0x391d028b, 0x399cd362,     /* 26 deg. */
    0x3a1c5c57, 0x3a9b9cca, 0x3b1a941c, 0x3b9941b1,     /* 27 deg. */
    0x3c17a4e8, 0x3c95bd26, 0x3d1389cb, 0x3d910a3c,     /* 28 deg. */
    0x3e0e3ddc, 0x3e8b240e, 0x3f07bc37, 0x3f8405bc,     /* 29 deg. */
    0x40000000, 0x407baa6a, 0x40f7045f, 0x41720d46,     /* 30 deg. */
    0x41ecc484, 0x42672981, 0x42e13ba4, 0x435afa54,     /* 31 deg. */
    0x43d464fb, 0x444d7aff, 0x44c63bcb, 0x453ea6c7,     /* 32 deg. */
    0x45b6bb5e, 0x462e78f9, 0x46a5df03, 0x471cece7,     /* 33 deg. */
    0x4793a210, 0x4809fdeb, 0x487fffe4, 0x48f5a767,     /* 34 deg. */
    0x496af3e2, 0x49dfe4c2, 0x4a547976, 0x4ac8b16b,     /* 35 deg. */
    0x4b3c8c12, 0x4bb008d9, 0x4c232730, 0x4c95e688,     /* 36 deg. */
    0x4d084651, 0x4d7a45fe, 0x4debe4fe, 0x4e5d22c6,     /* 37 deg. */
    0x4ecdfec7, 0x4f3e7875, 0x4fae8f43, 0x501e42a5,     /* 38 deg. */
    0x508d9211, 0x50fc7cfb, 0x516b02d9, 0x51d92321,     /* 39 deg. */
    0x5246dd49, 0x52b430c9, 0x53211d18, 0x538da1ae,     /* 40 deg. */
    0x53f9be05, 0x54657194, 0x54d0bbd6, 0x553b9c45,     /* 41 deg. */
    0x55a6125c, 0x56101d94, 0x5679bd6c, 0x56e2f15d,     /* 42 deg. */
    0x574bb8e6, 0x57b41384, 0x581c00b3, 0x58837ff4,     /* 43 deg. */
    0x58ea90c4, 0x595132a2, 0x59b76510, 0x5a1d278d,     /* 44 deg. */
    0x5a82799a, 0x5ae75ab9, 0x5b4bca6c, 0x5bafc837,     /* 45 deg. */
    0x5c13539b, 0x5c766c1c, 0x5cd91140, 0x5d3b428c,     /* 46 deg. */
    0x5d9cff83, 0x5dfe47ad, 0x5e5f1a91, 0x5ebf77b5,     /* 47 deg. */
    0x5f1f5ea1, 0x5f7ecedd, 0x5fddc7f3, 0x603c496c,     /* 48 deg. */
    0x609a52d3, 0x60f7e3b0, 0x6154fb91, 0x61b19a00,     /* 49 deg. */
    0x620dbe8b, 0x626968be, 0x62c49827, 0x631f4c54,     /* 50 deg. */
    0x637984d4, 0x63d34137, 0x642c810c, 0x648543e4,     /* 51 deg. */
    0x64dd8950, 0x653550e2, 0x658c9a2d, 0x65e364c4,     /* 52 deg. */
    0x6639b03b, 0x668f7c25, 0x66e4c818, 0x673993a9,     /* 53 deg. */
    0x678dde6e, 0x67e1a7ff, 0x6834eff3, 0x6887b5e2,     /* 54 deg. */
    0x68d9f964, 0x692bba14, 0x697cf78a, 0x69cdb162,     /* 55 deg. */
    0x6a1de737, 0x6a6d98a4, 0x6abcc547, 0x6b0b6cbd,     /* 56 deg. */
    0x6b598ea3, 0x6ba72a98, 0x6bf4403b, 0x6c40cf2c,     /* 57 deg. */
    0x6c8cd70b, 0x6cd8577a, 0x6d23501b, 0x6d6dc08f,     /* 58 deg. */
    0x6db7a87a, 0x6e010780, 0x6e49dd45, 0x6e92296e,     /* 59 deg. */
    0x6ed9eba1, 0x6f212385, 0x6f67d0c1, 0x6fadf2fc,     /* 60 deg. */
    0x6ff389df, 0x70389514, 0x707d1443, 0x70c10718,     /* 61 deg. */
    0x71046d3e, 0x71474660, 0x7189922c, 0x71cb504e,     /* 62 deg. */
    0x720c8075, 0x724d224f, 0x728d358c, 0x72ccb9db,     /* 63 deg. */
    0x730baeed, 0x734a1475, 0x7387ea23, 0x73c52fab,     /* 64 deg. */
    0x7401e4c1, 0x743e0918, 0x74799c66, 0x74b49e5f,     /* 65 deg. */
    0x74ef0ebc, 0x7528ed32, 0x7562397a, 0x759af34c,     /* 66 deg. */
    0x75d31a61, 0x760aae73, 0x7641af3d, 0x76781c7a,     /* 67 deg. */
    0x76adf5e6, 0x76e33b3f, 0x7717ec41, 0x774c08ab,     /* 68 deg. */
    0x777f903c, 0x77b282b3, 0x77e4dfd2, 0x7816a759,     /* 69 deg. */
    0x7847d909, 0x787874a7, 0x78a879f4, 0x78d7e8b6,     /* 70 deg. */
    0x7906c0b0, 0x793501a9, 0x7962ab67, 0x798fbdb0,     /* 71 deg. */
    0x79bc384d, 0x79e81b06, 0x7a1365a5, 0x7a3e17f2,     /* 72 deg. */
    0x7a6831ba, 0x7a91b2c7, 0x7aba9ae6, 0x7ae2e9e4,     /* 73 deg. */
    0x7b0a9f8d, 0x7b31bbb2, 0x7b583e21, 0x7b7e26aa,     /* 74 deg. */
    0x7ba3751d, 0x7bc8294d, 0x7bec430b, 0x7c0fc22a,     /* 75 deg. */
    0x7c32a67e, 0x7c54efdc, 0x7c769e18, 0x7c97b109,     /* 76 deg. */
    0x7cb82885, 0x7cd80464, 0x7cf7447f, 0x7d15e8ad,     /* 77 deg. */
    0x7d33f0ca, 0x7d515caf, 0x7d6e2c37, 0x7d8a5f40,     /* 78 deg. */
    0x7da5f5a5, 0x7dc0ef44, 0x7ddb4bfc, 0x7df50bab,     /* 79 deg. */
    0x7e0e2e32, 0x7e26b371, 0x7e3e9b4a, 0x7e55e59e,     /* 80 deg. */
    0x7e6c9251, 0x7e82a146, 0x7e981262, 0x7eace58a,     /* 81 deg. */
    0x7ec11aa5, 0x7ed4b198, 0x7ee7aa4c, 0x7efa04a8,     /* 82 deg. */
    0x7f0bc097, 0x7f1cde01, 0x7f2d5cd1, 0x7f3d3cf4,     /* 83 deg. */
    0x7f4c7e54, 0x7f5b20df, 0x7f692483, 0x7f76892f,     /* 84 deg. */
    0x7f834ed0, 0x7f8f7559, 0x7f9afcb9, 0x7fa5e4e1,     /* 85 deg. */
    0x7fb02dc6, 0x7fb9d759, 0x7fc2e18e, 0x7fcb4c5b,     /* 86 deg. */
    0x7fd317b4, 0x7fda4391, 0x7fe0cfe7, 0x7fe6bcb0,     /* 87 deg. */
    0x7fec09e3, 0x7ff0b77a, 0x7ff4c56f, 0x7ff833bd,     /* 88 deg. */
    0x7ffb0260, 0x7ffd3154, 0x7ffec096, 0x7fffb026,     /* 89 deg. */
    0x7fffffff                                          /* 90 deg. */
};


/*----------------------------------------------------------------------
 *  Given an angle t, calculate the values cos(t) and sin(t).  The first
 *  two arguments are pointers to the cosine and sine values,
 *  respectively.  Each value is a 32-bit fixed point value.  Argument t
 *  specifies the angle in degrees, and is a fixed-point number with 2
 *  bits of fraction.  Hence, angles can be specified to 1/4 degree.
 *  Argument n specifies the number of bits of fraction in the fixed
 *  point values for the sine and cosine.  For example, n = 8 specifies
 *  that the 8 LSBs of the cosine and sine values are the fractional
 *  parts of those values.  The range of allowed values for n is 1 to
 *  30.
 *
 *  The return value is angle t, which is unmodified if argument t was
 *  in the range 0 to 360*4-1. Otherwise, the return value is t
 *  converted to the equivalent angle in the range 0 to 360*4-1.
 *----------------------------------------------------------------------
 */
static long trigfun(cost, sint, t, n)
long *cost, *sint;  /* pointers to cosine and sine variables */
long t;             /* angle in degrees (fixed pt, 2 LSBs = fraction) */
short n;            /* no. of bits of fraction in cost and sint */
{
    /* restrict angle t to range 0 to 360 degrees */
    if ((t %= 360*4) < 0)
        t += 360*4;
    if (t < 180*4) {
        if (t < 90*4) {
            *cost =  (sintbl[90*4-t]  >> 31-n);
            *sint =  (sintbl[t]       >> 31-n);
        } else {
            *cost = -(sintbl[t-90*4]  >> 31-n);
            *sint =  (sintbl[180*4-t] >> 31-n);
        }
    } else {
        if (t < 270*4) {
            *cost = -(sintbl[270*4-t] >> 31-n);
            *sint = -(sintbl[t-180*4] >> 31-n);
        } else {
            *cost =  (sintbl[t-270*4] >> 31-n);
            *sint = -(sintbl[360*4-t] >> 31-n);
        }
    }
    return (t);
}


/*----------------------------------------------------------------------
 * rotvec function:
 *
 *  This function rotates three orthogonal unit vectors in three
 *  dimensions.  Given the x, y and z components of three orthogonal
 *  unit vectors u, v and w in world coordinate space, the vectors are
 *  rigidly rotated in the specified plane.  The rotation can be
 *  specified relative to the world xyz coordinate system (the xy, yz or
 *  zx plane), or relative to the orthogonal unit vectors themselves
 *  (the uv, vw or wu plane).
 *
 *  Vectors u, v and w are typically used to represent the viewer's
 *  orientation in world space.  Rotations in the wu, vw and uv planes
 *  correspond to yaw, pitch and roll movements, respectively, in the
 *  viewer's frame of reference.  In each case, two designated vectors
 *  are rigidly rotated about the third, which remains fixed.
 *
 *  By convention, the viewing coordinate origin is located in the
 *  center of the screen.  The +x axis points to the right, the +y axis
 *  points down, and the +z axis points into the screen.  Orthogonal
 *  unit vectors u, v and w are aligned with viewing coordinate axes x,
 *  y and z, respectively.
 *
 *  By convention, directions in world coordinates are defined as
 *  follows:  the +x direction is east, the +y direction is north, and
 *  the +z direction is up.
 *
 *  Argument t specifies the angle of rotation in degrees, and is
 *  expressed as a signed fixed-point number with 2 bits of fraction.
 *  The sign of angle t determines the direction of rotation.
 *
 *  Argument vec is a pointer to structure containing the u, v and w
 *  vectors.  The function assumes that the x, y and z components of
 *  orthogonal unit vectors u, v and w are stored consecutively in
 *  memory in the following order:  Ux, Uy, Uz, Vx, Vy, Vz, Wx, Wy and
 *  Wz.  Each of the 9 components is a value in the range -1 to +1, and
 *  is represented as a 32-bit fixed point number with 30 bits of
 *  fraction.
 *
 *  Argument mode indicates in which plane the unit vectors are to be
 *  rotated.  Assuming a positive angle t, for example, the following
 *  rotations may be specified:
 *
 *    mode = VU   Roll to left by rotating in uv plane by -t degrees.
 *                (The v vector turns toward the u vector, which in turn
 *                rotates away from the v vector.)
 *
 *    mode = WU   Yaw to right by rotating in wu plane by +t degrees.
 *                (The w vector turns toward the u vector, which in turn
 *                rotates away from the w vector.)
 *
 *    mode = UV   Roll to right by rotating in uv plane by +t degrees.
 *                (The u vector turns toward the v vector, which in turn
 *                rotates away from the u vector.)
 *
 *    mode = WV   Pitch downward by rotating in vw plane by -t degrees.
 *                (The w vector turns toward the v vector, which in turn
 *                rotates away from the w vector.)
 *
 *    mode = UW   Yaw to left by rotating in wu plane by -t degrees.
 *                (The u vector turns toward the w vector, which in turn
 *                rotates away from the u vector.)
 *
 *    mode = VW   Pitch upward by rotating in vw plane by +t degrees.
 *                (The v vector turns toward the w vector, which in turn
 *                rotates away from the v vector.)
 *
 *    mode = YX   Rotate clockwise level to ground by rotating in xy
 *                plane by -t degrees.  (The y components of the three
 *                vectors are rotated toward the x components, which in
 *                turn are rotated away from the y components).
 *
 *    mode = ZX   Rotate in zx plane by +t degrees.  (The z components
 *                of the three vectors are rotated toward the x
 *                components, which in turn are rotated away from the z
 *                components).
 *
 *    mode = XY   Rotate counterclockwise level to ground by rotating in
 *                xy plane by +t degrees.  (The x components of the
 *                three vectors are rotated toward the y components,
 *                which in turn are rotated away from the x components).
 *
 *    mode = ZY   Rotate in yz plane by -t degrees.  (The z components
 *                of the three vectors are rotated toward the y
 *                components, which in turn are rotated away from the z
 *                components).
 *
 *    mode = XZ   Rotate in zx plane by -t degrees.  (The x components
 *                of the three vectors are rotated toward the z
 *                components, which in turn are rotated away from the x
 *                components).
 *
 *    mode = YZ   Rotate in yz plane by +t degrees.  (The y components
 *                of the three vectors are rotated toward the z
 *                components, which in turn are rotated away from the y
 *                components).
 *
 *  If a negative angle t is specified, the direction of rotation is
 *  opposite from that described for each case above.  The mode
 *  constants are defined as follows:  UV = 4, VU = 1, VW = 9, WV = 6,
 *  WU = 2, UW = 8, XY = 20, YX = 17, YZ = 25, ZY = 22, ZX = 18, and
 *  XZ = 24.
 *
 *  The return value is angle t, which is unmodified if argument t was
 *  in the range 0 to 360*4-1. Otherwise, the return value is t
 *  converted to the equivalent angle in the range 0 to 360*4-1.
 *----------------------------------------------------------------------
 */
long rotvec(mode, t, vec)
short mode;
long t;
UNITVEC *vec;
{
    double cost, sint, x1, x2;
    int i, incr;
    long cs, sn, *vec1, *vec2;

    /* get cos(t) and sin(t) */
    t = trigfun(&cs, &sn, t, 30);
    cost = ldexp(cost=cs, -30);
    sint = ldexp(sint=sn, -30);

    /* decode mode */
    switch (mode) {
    case UV:  incr = 1;
              vec1 = &vec->ux;
              vec2 = &vec->vx;
              break;
    case VU:  incr = 1;
              vec1 = &vec->vx;
              vec2 = &vec->ux;
              break;
    case VW:  incr = 1;
              vec1 = &vec->vx;
              vec2 = &vec->wx;
              break;
    case WV:  incr = 1;
              vec1 = &vec->wx;
              vec2 = &vec->vx;
              break;
    case WU:  incr = 1;
              vec1 = &vec->wx;
              vec2 = &vec->ux;
              break;
    case UW:  incr = 1;
              vec1 = &vec->ux;
              vec2 = &vec->wx;
              break;
    case XY:  incr = 3;
              vec1 = &vec->ux;
              vec2 = &vec->uy;
              break;
    case YX:  incr = 3;
              vec1 = &vec->uy;
              vec2 = &vec->ux;
              break;
    case YZ:  incr = 3;
              vec1 = &vec->uy;
              vec2 = &vec->uz;
              break;
    case ZY:  incr = 3;
              vec1 = &vec->uz;
              vec2 = &vec->uy;
              break;
    case ZX:  incr = 3;
              vec1 = &vec->uz;
              vec2 = &vec->ux;
              break;
    case XZ:  incr = 3;
              vec1 = &vec->ux;
              vec2 = &vec->uz;
              break;
    default:  return (t);
    }

    /* rotate the vectors */
    for (i = 1; i <= 3; ++i) {
        x1 = *vec1;
        x2 = *vec2;
        *vec1 =  cost * x1 + sint * x2;
        *vec2 = -sint * x1 + cost * x2;
        vec1 = &vec1[incr];
        vec2 = &vec2[incr];
    }
    return (t);
}

