Angus Dorbie (dorbie++at++multipass)
Mon, 29 Dec 1997 14:45:33 -0800
You'll find attached the flybox code and the include file,
Use the include file as a guide to using the flybox module.
The SGI ioctl stuff changed slightly with IRIX 6.2 so
there is a minor gotcha, so beware. I borrowed the flybox
code but tried to make it easily useable via the function
calls declared in BgPub.h. It's real simple and requires
no explanation.
Also find my performer math based motion model attached.
It's a vector based model which has no concept of up, it
doesn't have any of those Euler->quat problems people
seem to be plagued with.
Create a pov class and use reset and accelerate to move,
call simulate with the joystick information and call
posview to obtain a tethered position based on the mode
SUB_VIEW_*. It's dead simple and fairly elegant.
Here's an example main loop which does all the vehicle positioning
after some trivial setup which should be obvious.
// vehicle is a pov class, matey is a pfMatrix, viewcoord is a pfcoord
ReadFLYBOX(&StickX, &StickY, &StickT, &AC1, &AC2);
vehicle->simulate(StickX, StickY, StickT, deltaTime);
vehicle->accelerate(AC2);
vehicle->speedlimit(AC2);
vehicle->posview(&viewcoord, SUB_VIEW_POV);
matey.makeCoord(&viewcoord);
channel->setViewMat(matey);
Cheers,Angus.
--
#include <Performer/pr.h> #include <Performer/prmath.h> #include <Performer/pr/pfLinMath.h> #include <Performer/pf/pfDCS.h> #include <Performer/pf/pfScene.h>
#define SUB_VIEW_POV 0 #define SUB_VIEW_WINGMAN 1 #define SUB_VIEW_TETHERHIGH 2 #define SUB_VIEW_TETHER 3 #define SUB_VIEW_FWDSHIFT 4 #define NUMTRAILS 240
class pov {
pfVec3 forward; pfVec3 up; pfCoord subcoord, wing[NUMTRAILS]; float accelRate; float MaxSpeed; float MinSpeed; float PitchAuthority; float RollAuthority; float TwistAuthority; float speed; float Trange, Thead, Trate; float TPamp, TPloop, TPrate; int wingcount;
public:
pov(void);
void reset(void); void accelerate(float thrust); void speedlimit(float factor); void simulate(float joyx, float joyy, float twist, float dtime);
void posview( pfCoord *view, int mode ); };
#include <stdlib.h> #include <string.h> #include <iostream.h> #include "pov.h"
pov::pov(void) { pfVec4 colour;
accelRate = .1f; MaxSpeed = 10.0f; MinSpeed = -10.0f; PitchAuthority = 10.0f; RollAuthority = 10.0f; TwistAuthority = 10.0f; Trange = 10.0f; Thead = 0.0f; Trate = 30.0f; TPamp = 0.4f; TPrate = 17.0f; wingcount = 0;
reset(); }
void pov::reset(void) { int i;
subcoord.xyz.set(0.0f, -100.0f, 13.0f); subcoord.hpr.set(0.0f, 0.0f, 0.0f); forward.set(0.0f, 1.0f, 0.0f); up.set(0.0f, 0.0f, 1.0f); speed = 0.0f; for(i=0;i<NUMTRAILS; i++) { simulate(0.0f, 0.0f, 0.0f, 0.016666667f); } }
void pov::accelerate(float thrust) { speed += thrust * accelRate; if(speed > MaxSpeed) speed = MaxSpeed; else if(speed < MinSpeed) speed = MinSpeed; }
void pov::speedlimit(float factor) { if(factor > 0.0f) { if(speed > MaxSpeed * factor) speed = MaxSpeed*factor; } else { if(speed < MinSpeed*factor*-1.0f) speed = MinSpeed*factor*-1.0f; } }
void pov::simulate(float joyx, float joyy, float twist, float dtime) { int i; float speedorientscale; pfVec3 starboard, norolup, norolstbd, calcvec; pfMatrix calcmat; float dot1, dot2, roll1, roll2;
if(joyx > 1.0f) joyx = 1.0f; if(joyy > 1.0f) joyy = 1.0f; if(twist > 1.0f) twist = 1.0f; if(joyx < -1.0f) joyx = -1.0f; if(joyy < -1.0f) joyy = -1.0f; if(twist < -1.0f) twist = -1.0f;
// pitch and roll & twist the viewpoint // use the forward and up vectors for this
// speedorientscale determines actual orientation // authorities based on vehicle speed speedorientscale = speed * .5f + 1.5f; if(speedorientscale < 1.5f) speedorientscale = 1.5f;
calcmat.makeRot(TwistAuthority * speedorientscale * dtime * -twist, up[0], up[1], up[2]); forward.xformVec(forward, calcmat);
starboard.cross(forward, up); calcmat.makeRot(PitchAuthority * speedorientscale * dtime * -joyy, starboard[0], starboard[1], starboard[2]); forward.xformVec(forward, calcmat); up.xformVec(up, calcmat);
calcmat.makeRot(RollAuthority * speedorientscale * dtime * joyx, forward[0], forward[1], forward[2]); up.xformVec(up, calcmat);
// ensure forward & up are at right angles by generating the // cross products & then back starboard.cross(forward, up); up.cross(starboard, forward);
// normalise forward & up vectors forward.normalize(); up.normalize();
// move the view forward by speed along the orientation vector subcoord.xyz += forward * (speed * dtime);
// convert vector representation to Euler // heading subcoord.hpr[0] = pfArcTan2(-forward[0], forward[1]); // pitch subcoord.hpr[1] = pfArcSin(forward[2]); // roll calcmat.makeEuler(subcoord.hpr[0], subcoord.hpr[1], 0.0f); calcvec.set(0.0f, 0.0f, 1.0f); norolup.xformVec(calcvec, calcmat); calcvec.set(1.0f, 0.0f, 0.0f); norolstbd.xformVec(calcvec, calcmat);
// the angle between norolup and up // and between norolstbd and up now // holds the required roll
dot1 = up.dot(norolup); if (dot1 > 1.0f) dot1 = 1.0f; if (dot1 < -1.0f) dot1 = -1.0f; dot2 = up.dot(norolstbd); if (dot2 > 1.0f) dot2 = 1.0f; if (dot2 < -1.0f) dot2 = -1.0f; roll1 = pfArcCos( dot1 ); roll2 = pfArcCos( dot2 );
if(roll2 > 90.0f) roll1 = -roll1; subcoord.hpr[2] = roll1;
// update wingman data for posview method wing[wingcount] = subcoord; wingcount++; if(wingcount >= NUMTRAILS) wingcount = 0;
// update tether data for posview method TPloop += TPrate * dtime; while(TPloop > 360.0f) TPloop -= 360.0f;
Thead += Trate * dtime; while(Thead > 360.0f) Thead -= 360.0f;
}
void pov::posview( pfCoord *view, int mode ) { float cval, sval, pval, attnval;
switch(mode) { case(SUB_VIEW_POV): view->hpr = subcoord.hpr; view->xyz = subcoord.xyz; break; case(SUB_VIEW_WINGMAN): view->hpr = wing[wingcount].hpr; view->xyz = wing[wingcount].xyz; break; case(SUB_VIEW_TETHERHIGH): pfSinCos(Thead, &pval, &attnval); view->hpr.set(-Thead+180.0f, -80.0f, 0.0f); pfSinCos(Thead, &sval, &cval); view->xyz.set(Trange * sval *.2f, Trange * cval *.2f, Trange * 1.8f); view->xyz += subcoord.xyz; break; case(SUB_VIEW_TETHER): pfSinCos(Thead, &pval, &attnval); view->hpr.set(-Thead+180.0f, -pfArcSin(pval * TPamp), 0.0f); pfSinCos(Thead, &sval, &cval); view->xyz.set(Trange * sval, Trange * cval, Trange * pval * TPamp); view->xyz += subcoord.xyz; break; case(SUB_VIEW_FWDSHIFT): view->hpr = subcoord.hpr; view->xyz = subcoord.xyz + forward*1.0f; break; } }
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <signal.h> #include <string.h> #include <unistd.h>
#include <sys/schedctl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/prctl.h>
#include <fcntl.h> #include <termio.h>
#include "BgPriv.h"
#define CLAMP(var) (var < .05 && var > -.05 ? 0 : var)
void rf_quit(void);
// Global data static bglv bgdata;
RS_ERR rs_err;
void ExitFLYBOX(void) { close_lv (&bgdata); }
void ReadFLYBOX(float *xjoy, float *yjoy, float *yaw, float *accel1, float *accel2 ) { int st; float scale, f, s; float sin, cos; int i, j;
st = w_lv(bgdata.sp_fd, "o");
st = r_lv(&bgdata);
// joy x *xjoy = bgdata.ain[0]; // joy y *yjoy = bgdata.ain[1]; // joy twist *yaw = bgdata.ain[2]; // lever 1 *accel1 = bgdata.ain[3]; // lever2 *accel2 = bgdata.ain[4];
/* for ( j = 0; j <=2; j++ ) { if ( bgdata.dig_in & 0x10 << j ) { for ( i = 0; i < 8; i++ ) { if ( (bgdata.din[j]>>i) & 0x1 ) printf("1"); else printf("0"); } printf(" "); } }
printf("\n"); */ }
void InitFLYBOX () { int st;
// Defaults to 5 analog, and 16 discretes bgdata.analog_in = 0; bgdata.analog_in = AIC1 | AIC2 | AIC3 | AIC4 | AIC5;
bgdata.dig_in = 0; bgdata.dig_in = DIC1 | DIC2;
// Set the baud rate bgdata.baud = BAUD192;
// Open the port & drivers st = open_lv (&bgdata); if (st < 0) { printf("Unable to open port\n"); exit(-1); }
// Send the init string st = init_lv(&bgdata); if ( st < 0 ) { check_setup(&bgdata); printf("Invalid setup requested. Bye\n"); exit(-1); }
}
int open_lv (bglv *bgp) { int st; char port[4]; char pt[32]; char *ep;
rs_err.wrt = 0; rs_err.rd = 0; rs_err.len = 0; rs_err.nl = 0; rs_err.cycles = 0; rs_err.thou = 0;
// Initialize port if ( ep = getenv("FBPORT") ) sprintf(pt,"%s",ep); else sprintf(pt,"%s",FBPORT);
port[0] = pt[strlen(pt)-1]; bgp->port = atoi(port);
printf ("****** trying to open port %s\n", pt);
bgp->sp_fd = open(pt, O_RDWR|O_NDELAY);
if (bgp->sp_fd < 0) { perror(pt); return(-1); }
st = set_baud(bgp->sp_fd);
st = check_rev(bgp); if ( st < 0 ) return(st); else return(0);
} // End open_lv
int set_baud (int sp_fd) { struct termio tios; int st;
st = ioctl(sp_fd,TCGETA,&tios); tios.c_iflag = IGNBRK|IXON|IXOFF; tios.c_oflag = 0; tios.c_lflag = ICANON;
//tios.c_cflag = B19200|CS8|CREAD|CLOCAL; tios.c_cflag = CS8|CREAD|CLOCAL; tios.c_ospeed = B19200;
st = ioctl(sp_fd,TCSETAF,&tios); return(st);
} // End set_baud
int init_lv(bglv *bgp) { char c1, c2, c3, str[5]; int st, i;
st = check_setup(bgp); if ( st < 0 ) return(st);
// Compute the number of channels requested, and the // appropriate string length.
// Analog inputs bgp->n_analog_in = 0; for ( i=0; i < 8; i++) if ( (bgp->analog_in >> i) & 0x1 ) bgp->n_analog_in++;
// Digital inputs switch(bgp->dig_in) { case 0x0: bgp->n_dig_in = 0; break; case 0x10: case 0x20: case 0x40: bgp->n_dig_in = 8; break; case 0x30: case 0x50: case 0x60: bgp->n_dig_in = 16; break; case 0x70: bgp->n_dig_in = 24; break; }
// Digital outputs switch(bgp->dig_out) { case 0x0: bgp->n_dig_out = 0; break; case 0x10: case 0x20: case 0x40: bgp->n_dig_out = 8; break; case 0x30: case 0x50: case 0x60: bgp->n_dig_out = 16; break; case 0x70: bgp->n_dig_out = 24; break; }
// Analog outputs bgp->n_analog_out = 0; if ( bgp->analog_out > 0 ) { for ( i=0; i < 3; i++) if ( (bgp->analog_out >> i) & 0x1 ) bgp->n_analog_out++; }
// Set the string length for receiving data bgp->str_len = 2 + (2*bgp->n_analog_in) + (bgp->n_dig_in/4);
// First character has the baud rate and the lower 4 analog ins. c1 = bgp->baud; c1 |= (bgp->analog_in & 0xf);
// Second character has the digital inputs and the upper 4 analog ins c2 = bgp->dig_in; c2 |= (bgp->analog_in & 0xf0) >> 4;
if ( bgp->Rev.major == 3 ) { str[0] = 's';
// Third character (for rev 3 eproms only, has the digital outs (-F) // and analog outs (-3G) c3 = bgp->analog_out & 0xf; c3 |= bgp->dig_out & 0xf0;
// Add the OFFSET to each character to make sure they are not control // characters str[1] = c1 + OFFSET; str[2] = c2 + OFFSET; str[3] = c3 + OFFSET; str[4] = '\0'; st = w_lv(bgp->sp_fd, str);
// Make sure that the LV got the setup ! st = get_ack(bgp->sp_fd);
// If we have a rev 3.00 eprom, just don't check the return // value - just proceed and assume things are OK. // (Bug fixed in 3.01) if ( bgp->Rev.bug != 0 ) { if ( st < 0 ) return(st); }
} else if ( bgp->Rev.major == 2 ) { if ( bgp->Rev.minor == 2 ) { // For rev 2.2 EPROMS use an 'R' and no offset -- so make // sure c1 and c2 are not flow control characters ! str[0] = 'R'; str[1] = c1; str[2] = c2; } else if ( bgp->Rev.minor >= 3 ) { // For rev 2.3 EPROMS use an 'r' and offset the characters str[0] = 'r'; str[1] = c1 + OFFSET; str[2] = c2 + OFFSET; } str[3] = '\0'; st = w_lv(bgp->sp_fd, str); } st = set_baud(bgp->sp_fd); return(0); }
int get_ack (int sp_fd) { int st; int i = 0; int chars = 2; char str[36]; st = read(sp_fd,str,chars); if (st < 0) { printf("get_ack(): read error\n"); return(-1); } while ( st != 2 && i < 10000) { sginap(1); st = read(sp_fd,str,chars); i++; } if ( i > 10000 ) printf("Timeout %d chars in buffer \n", chars); if (str[0] == 'a' ) { printf("Setup OK\n"); return (0); } else if (str[0] == 'f' ) { printf("Setup failed\n"); return (-1); } else { printf("Unexpected respons: %s\n", str); return (-2); } //return(st); }
static char Cpy[] = "Copyright (c), BG Systems";
int check_rev(bglv *bgp) { int st; int chars_read = 0; char str[64];
/* * Send a "T" and see if the Box responds */ st = write(bgp->sp_fd, "T", 2); sginap(100); chars_read = read(bgp->sp_fd, str, 44);
/* * If chars_read <= 0, looks like we have a Rev 1.x EPROM */ if (chars_read <= 0) { no_answer(); return(-1); } else { /* * Check the string length */ if ( chars_read != 44 ) { printf("Unexpected characters: %d %s\n", chars_read, str); return(-1); } else { /* * Check that it is the Copyright string */ if ( strncmp(str, Cpy, strlen(Cpy)) != 0 ) { printf("Unexpected characters: %d %s\n", chars_read, str); return(-1); } else { /* * If we go this far, we should have the right string */ bgp->Rev.year = parse_year(str); bgp->Rev.major = str[38]-48; bgp->Rev.minor = str[40]-48; bgp->Rev.bug = str[41]-48; bgp->Rev.alpha = str[42]; /* printf("%s %d Revision %d.%d%d%c\n", Cpy, bgp->Rev.year, bgp->Rev.major, bgp->Rev.minor, bgp->Rev.bug, bgp->Rev.alpha ); */ } } } return (bgp->Rev.major); }
int parse_year(char *s) { int i = 0; char yr[12];
while ( *s != '1' ) *s++; yr[i] = *s; while ( *s != ' ' && *s != ',' ) yr[i++] = *s++; yr[i] = '\0'; return(atoi(yr)); }
int check_setup(bglv *bgp) { int i; int st = 0;
/* * This routine checks the EPROM revision against the * requested setup, and attempts to identify inconsistencies ! */
if ( bgp->Rev.major == 2 ) { if ( bgp->analog_out != 0x0 ) { printf(" Analog outputs not supported by LV816\n"); st = -1; } if ( bgp->dig_out != 0x0 ) { printf(" Digital outputs not supported by LV816\n"); st = -2; } if ( bgp->dig_in & 0x40 ) { printf(" Digital inputs 19-24 not supported by LV816\n"); st = -3; } } else if ( bgp->Rev.major == 3 ) { switch(bgp->Rev.alpha) { case 'e': printf("LV824-E\n"); if ( bgp->analog_out != 0x0 ) { printf(" Analog outputs not supported\n"); st = -1; } if ( bgp->dig_out != 0x0 ) { printf(" Digital outputs not supported\n"); st = -2; } break; case 'f': printf("LV824-F\n"); if ( bgp->analog_out != 0x0 ) { printf(" Analog outputs not supported\n"); st = -2; } break; case 'g': printf("LV824-G\n"); break; default: st = -3; printf("Not an LV824 board\n"); break; } if ( st < 0 ) return(st); /* * Check also for conflict in the digital channels */
if ( bgp->dig_in && bgp->dig_out ) { for ( i = 0; i < 3; i++ ) { if ( ( (bgp->dig_in >> i) &0x1 ) && ( (bgp->dig_out >> i) &0x1 ) ) {
printf("Invalid set-up requested.\n"); printf(" Digital input group %d AND output group %d selected\n", i+1, i+1);
printf("\n\n Digital channels can be set in groups of 8 as\n"); printf(" either inputs or outputs.\n"); printf(" Of course you can (for example) set the bottom 8\n"); printf(" to inputs DIC1 and the top 16 to outputs DOC2 | DOC3\n");
st = -5; return(st); } } } } return(st); }
void no_answer() { printf("\nWriting a 'T' to the Box produced no answer. \n"); printf("\n"); printf("The expected string was not returned from the BG box.\n"); printf("Here are some possible problems:\n"); printf(" 1. Check power to Box\n"); printf(" 2. Check the serial cable\n"); printf(" 3. Check the environment variable FBPORT\n"); printf(" - does it match the connected serial port ?\n"); printf(" 4. Is the serial port configured as a terminal ? \n"); printf(" - if so use \"System Manager\" to disconnect the port\n"); printf(" 5. You have an old FlyBox (serial no. less than 60) \n"); printf(" which has a revision 1.0 EPROM. Call BG Systems.\n");
printf("\n\n"); }
int w_lv(int sp_fd, char *mode) { int st;
st = write(sp_fd, mode, strlen(mode)); if (st < 0) rs_err.wrt++; return(st); }
int r_lv(bglv *bgp) { int st; int i = 0; char str[36]; rs_err.cycles++; if( rs_err.cycles % 1000 == 0 ) { rs_err.cycles = 0; rs_err.thou++; } st = read(bgp->sp_fd,str,bgp->str_len); if (st < 0) { rs_err.rd++; printf("r_lv(): read error\n"); return(-1); } while ( st != bgp->str_len && i < 100) { st = read(bgp->sp_fd,str,bgp->str_len); i++; }
if ( i > 0 ) //printf("%d read attempts. \n", i); if (str[0] != 'B' || str[bgp->str_len - 1] != '\n') { printf("%d: %s\n", st, str); rs_err.rd++; return(-1); }
st = convert_serial(bgp, str); return st; }
int convert_serial(bglv *bgp, char *str) { int i, digp, j; int k = 0; float tmp[8];
digp = 0;
// Load the digital input values into dioval k = 1 + bgp->n_dig_in/4; if ( k > 1) { i = 1; for ( j = 2; j >= 0; j-- ) { if ( bgp->dig_in & 0x10<<j ) { digp = 0x0f & (str[i++]-0x21); digp = (digp << 4) | 0x0f & (str[i++]-0x21); bgp->din[j] = digp; } } }
// Load the 8 analog values into inbuf for (i = k; i < bgp->str_len - 2; i += 2) { digp = ((0x3f & (str[i]-0x21)) << 6) | (0x3f & (str[i+1]-0x21)); tmp[(i-k)/2] = -1.0 + (2.0 * digp/4095); } for ( i = 0, k = 0; k < 8; k++ ) { if ( bgp->analog_in >> k &0x1 ) { bgp->ain[k] = tmp[i]; i++; } }
digp = ((0x0f & (str[22]-0x21)) << 4) | (0x0f & (str[23]-0x21)); return (0); }
void close_lv(bglv *bgp) { int att; int st;
bgp->baud = BAUD192; st = init_lv(bgp);
att = 1000*rs_err.thou + rs_err.cycles; close(bgp->sp_fd); //printf("\nRead Attempts: %d\n", att); //printf("\nErrors Detected\n"); //printf("Read Write \n"); //printf("%5d %5d \n",rs_err.rd, rs_err.wrt);
}
#ifndef __BG_H__ #define __BG_H__
#include <Performer/pf.h>
#define FBPORT "/dev/ttyd2"
/* from lv3.h */ #include <time.h>
#define FLYBOX 1 #define BEEBOX 2 #define CEREALBOX 3 #define CAB 4 #define DRIVEBOX 5
#define FB_NOBLOCK 1 #define FB_BLOCK 2
#define AIC1 0x01 #define AIC2 0x02 #define AIC3 0x04 #define AIC4 0x08 #define AIC5 0x10 #define AIC6 0x20 #define AIC7 0x40 #define AIC8 0x80
#define AOC1 0x01 #define AOC2 0x02 #define AOC3 0x04
#define DIC1 0x10 #define DIC2 0x20 #define DIC3 0x40
#define DOC1 0x10 #define DOC2 0x20 #define DOC3 0x40
#define BAUD576 0x70 #define BAUD384 0x60 #define BAUD192 0x50 #define BAUD96 0x40 #define BAUD48 0x30 #define BAUD24 0x20 #define BAUD12 0x10
#define OFFSET 0x21
/* * Define some commands */
#define BURST 'B' /* Burst mode */ #define BURST_SET 'b' /* Burst mode rate set */ #define CONT 'c' /* Continuous buffered */ #define DEFAULT 'd' /* Reset to Default */ #define PACKET 'p' /* One input and one output */ #define ONCE 'o' /* One input */ #define ONCE_CS 'O' /* One input with check sum */ #define RESET_FB 'r' /* Reset 3 chars with offset */ #define RESET_FB_O 'R' /* Reset (rev 2.2 no offset) */ #define STOP 'S' /* Stop burst mode */ #define SETUP 's' /* Setup rev 3.0 eprom */ #define TEST1 'T' /* Test (and copyright) */ #define TEST2 't' /* Test (and copy, and rev #) */
typedef struct rs_struct { int wrt; /* write error */ int rd; /* read error */ int len; /* string length error */ int nl; /* last char error */ int cycles; /* numer of cycles */ int thou; /* thousands of cycles */ } RS_ERR;
typedef struct REVISION { int major; /* Software major revision */ int minor; /* Software minor revision */ int bug; /* Software bug revision */ char alpha; /* EPROM alpha revision */ int year; }revision;
/* * For v3.0 software, define a new structure */ typedef struct BGLV_STRUCT { int n_analog_in; /* Number of analog inputs (8 max) */ int analog_in; /* Analog input selector */ int n_dig_in; /* Number of digital inputs (24 max) */ int dig_in; /* Digital input selector */ int n_analog_out; /* Number of analog outputs (3 max) */ int analog_out; /* Analog out channel selector */ int n_dig_out; /* Number of digital outputs (24 max) */ int dig_out; /* Digital output selector */ float ain[8]; /* Analog input data */ int aout[3]; /* Analog output data */ int din[3]; /* Digital input data */ int dout[3]; /* Digital output data */ long count; int str_len; /* Length of string to expect */ int baud; /* Baud rate selected */ char mode[2]; /* Mode to send - rev 2.2 */ time_t tag; int port; int box_type; /* Device type */ int sp_fd; /* Serial port file descriptor */ revision Rev; /* Software major revision */ }bglv;
/* end from lv3.h */
void setup_lv (); int open_lv (bglv *); void close_lv(bglv *); int set_baud (int); int init_lv (bglv *); int get_ack (int);
int check_rev(bglv *); int parse_year(char *); int check_setup(bglv *); int w_lv(int, char *); int r_lv(bglv *); int convert_serial(bglv *, char *); void no_answer(void); void init_timer();
#endif
extern void InitFLYBOX(void);
extern void ReadFLYBOX(float *x, float *y, float *twist, float *ac1, float *ac2);
extern void ExitFLYBOX(void);
======================================================================= List Archives, FAQ, FTP: http://www.sgi.com/Technology/Performer/ Submissions: info-performer++at++sgi.com Admin. requests: info-performer-request++at++sgi.com
This archive was generated by hypermail 2.0b2 on Mon Aug 10 1998 - 17:56:28 PDT