/* * acm : an aerial combat simulator for X * Copyright (C) 1991-1998 Riley Rainey * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 dated June, 1991. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */ #ifndef WIN32 #include #endif #include #include #include "pm.h" #include "alarm.h" extern craftType *newCraft (void); extern int compileAircraftInventory (void); extern void initaim9 (void), initm61a1 (void), initmk82(void), initaim120(void); extern void buildExplosion (void); extern void placeObject (); extern void InitNavaidSystem (); extern void DMECheckAlarm (char *arg1, char *arg2); extern void resupplyCheck (char *arg1, char *arg2); extern void acm_srand PARAMS ((int seed)); extern void addAltitudeEntry (WorldCoordinates * w); extern int AddNavaid (char *ident, char *type, WorldCoordinates * w, char *magvar, double freq); extern int AddILS (char *ident, char *type, WorldCoordinates * w, WorldCoordinates * gsw, char *magvar, double freq, double loc_width, double loc_bearing, double gs_angle); /* * Defined in doViews.c : */ extern VPolygon **poly; extern long polyCount; void undersampleObject (VObject * obj, int rate); static char *errmsg = "Sorry, I cannot find the file %s in ../objects/, %s or %s\n"; extern void AddRunway (VMatrix * RWYtoXYZ, double length, double width, int flags, VPolygon *** poly, int *poly_count); /* * acm_fopen : Open an important acm data file (or die trying). */ char _acm_fopen_library_dir[2048]; FILE * acm_fopen(char *name, char *access) { FILE *f; char libname[256]; if ((f = fopen(name, access)) == (FILE *) NULL) { strcpy(libname, "../objects/"); strcat(libname, name); if ((f = fopen(libname, access)) == (FILE *) NULL) { strcpy(libname, _acm_fopen_library_dir); strcat(libname, name); if ((f = fopen(libname, access)) == (FILE *) NULL) { strcpy (libname, SRCDIR); strcat (libname, "/../objects/"); strcat (libname, name); if ((f = fopen(libname, access)) == (FILE *) NULL) { fprintf(stderr, errmsg, name, _acm_fopen_library_dir, libname); exit(1); } } } } return f; } /* * acm_find_file : Find an important acm data file (or die trying). */ char * acm_find_file(char *name) { static char pname[1024]; struct stat statbuf; strcpy(pname, name); if (stat(name, &statbuf) != 0) { strcpy(pname, "../objects/"); strcat(pname, name); if (stat(pname, &statbuf) != 0) { strcpy(pname, _acm_fopen_library_dir); strcat(pname, name); if (stat(pname, &statbuf) != 0) { fprintf(stderr, errmsg, name, _acm_fopen_library_dir); exit(1); } } } return pname; } enum state_t { SKIPPING_SPACES, ARGUMENT }; int split (char *s, int *argc, char *argv[]) { int done = 0; enum state_t state = SKIPPING_SPACES; *argc = 0; for (; done == 0; ++s) { switch (state) { case SKIPPING_SPACES: if (isspace (*s)) { } else if (isascii (*s)) { argv[*argc] = s; (*argc)++; state = ARGUMENT; } else if (*s == '\0') { done = 1; } break; case ARGUMENT: if (isspace (*s)) { *s = '\0'; state = SKIPPING_SPACES; } else if (*s == '\0') { *s = '\0'; done = 1; } } } return 0; } /* * readScene : Process the scene description file, returns zero if successful * or -1 otherwise. */ int readScene (char *ground, int len) { FILE *f, *f1; char file[256], *name, *filep; char *argv[32], line[1024]; char lat[64], lon[64], lat_lon[64]; double z, heading, freq, mag, length, width; int s, i = 0, j, undersample, depthcue, argc; craftType *c; extern char *sceneFile; /* from server.c */ VPoint scale, down, local_fwd, local_right, local_down; int done = 0; WorldCoordinates w, w1, w2, gsw; dis_world_coordinates xyz, xyz1; double loc_width, loc_bearing, gs_angle; VObject *object = NULL; VPolygon **rpoly; int rpoly_count; VMatrix RWYtoXYZ, XYZtoNED; /* initialize list of active viewers -- null list */ vl_head = vl_tail = NULL; vl_count = 0; rpoly = NULL; rpoly_count = 0; name = (sceneFile) ? sceneFile : "default-scene"; /* * The units used in the objects we've created ate expressed in FEET. * Internally, graphics objects should be METERS. */ scale.x = scale.y = scale.z = FEETtoMETERS (1.0); VSetReadObjectScale (&scale); f = acm_fopen (name, "r"); fscanf (f, "%s %s %lf %lf", lat, lon, &z, &heading); #ifdef FLAT_WORLD teamLoc[1].x = x * NM; teamLoc[1].y = y * NM; teamLoc[1].z = z; #else strcat (lat, lon); DISStringToLatLong (lat, &teamLatLon[1]); teamLatLon[1].z = FEETtoMETERS (z); #endif teamHeading[1] = DEGtoRAD (heading); fscanf (f, "%s %s %lf %lf", lat, lon, &z, &heading); #ifdef FLAT_WORLD teamLoc[2].x = x * NM; teamLoc[2].y = y * NM; teamLoc[2].z = z; #else strcat (lat, lon); DISStringToLatLong (lat, &teamLatLon[2]); teamLatLon[2].z = FEETtoMETERS (z); #endif teamHeading[2] = DEGtoRAD (heading); /* * Get the ground color (the first call to fgets() gets us to a new line). */ ground[0] = '\0'; if (fgets (ground, len, f) == (char *) NULL) { return -1; } if (fgets (ground, len, f) == (char *) NULL) { return -1; } len = strlen (ground); if (len > 0 && ground[len - 1] == '\n') { ground[len - 1] = '\0'; if (len > 0 && ground[len - 2] == '\r') { ground[len - 2] = '\0'; } } /* * Now get the NAVAID and runway records */ fgets (line, sizeof (line), f); done = 0; while (done == 0) { len = strlen (line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; } split (line, &argc, argv); /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * * RWY : Runway record * *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ if (strcmp (argv[0], "RWY") == 0) { /* * Parse Latitude/Longitude of both runway ends */ length = FEETtoMETERS (atoi (argv[4])); width = FEETtoMETERS (atoi (argv[5])); strcpy (lat_lon, argv[6]); strcat (lat_lon, argv[7]); DISStringToLatLong (lat_lon, &w); w.z = FEETtoMETERS (atoi (argv[3])); addAltitudeEntry (&w); strcpy (lat_lon, argv[8]); strcat (lat_lon, argv[9]); DISStringToLatLong (lat_lon, &w1); w1.z = w.z; DISWorldCoordinatesToGeocentric (&w, (dis_world_coordinates *) & xyz); mag = sqrt (xyz.x * xyz.x + xyz.y * xyz.y + xyz.z * xyz.z); DISWorldCoordinatesToGeocentric (&w1, (dis_world_coordinates *) & xyz1); /* * Average those two points to generate a midpoint that will be the * origin of a runway coordinate system. */ local_fwd.x = xyz1.x - xyz.x; local_fwd.y = xyz1.y - xyz.y; local_fwd.z = xyz1.z - xyz.z; xyz.x = (xyz.x + xyz1.x) / 2.0; xyz.y = (xyz.y + xyz1.y) / 2.0; xyz.z = (xyz.z + xyz1.z) / 2.0; DISGeocentricToWorldCoordinates (&xyz, &w2); GenerateWorldToLocalMatrix (&w2, &XYZtoNED); down.z = 1.0; down.x = down.y = 0.0; VReverseTransform_ (&down, &XYZtoNED, &local_down); mag = sqrt ( local_fwd.x * local_fwd.x + local_fwd.y * local_fwd.y + local_fwd.z * local_fwd.z ); local_fwd.x /= mag; local_fwd.y /= mag; local_fwd.z /= mag; /* * A basic property of Cross Products: k x i = j */ VCrossProd (&local_down, &local_fwd, &local_right); /* * Generate a transformation matrix to get from "runway" coordinates to * Geocentric. */ VIdentMatrix (&RWYtoXYZ); RWYtoXYZ.m[0][0] = local_fwd.x; RWYtoXYZ.m[1][0] = local_fwd.y; RWYtoXYZ.m[2][0] = local_fwd.z; RWYtoXYZ.m[0][1] = local_right.x; RWYtoXYZ.m[1][1] = local_right.y; RWYtoXYZ.m[2][1] = local_right.z; RWYtoXYZ.m[0][2] = local_down.x; RWYtoXYZ.m[1][2] = local_down.y; RWYtoXYZ.m[2][2] = local_down.z; RWYtoXYZ.m[0][3] = xyz.x; RWYtoXYZ.m[1][3] = xyz.y; RWYtoXYZ.m[2][3] = xyz.z; /* * AddRunway will return rpoly_count indicating the number of polygons * that were generated. rpoly is a malloc'ed vector of pointers to * the polygons. */ AddRunway (&RWYtoXYZ, length, width, 0, &rpoly, &rpoly_count); /* * optimize a bit here and put all the * runways for a given airport in the same object ... * object->name is the identifier of the current airport. */ if ((object != NULL) && (strcmp (object->name, argv[1]) == 0)) { /* * New runway at same airport as last .. add a polygon to the last object. */ object->polygon = (VPolygon **) realloc ( object->polygon, sizeof (VPolygon *) * (object->numPolys + rpoly_count)); if (!object->polygon) { printf ("memory allocation error\n"); exit (1); } for (j = 0; j < rpoly_count; ++j) { object->polygon[object->numPolys + j] = rpoly[j]; } object->numPolys += rpoly_count; free (rpoly); rpoly_count = 0; VComputeObjectExtent (object); } else { /* * New airport. New object and craftInfo. */ object = (VObject *) Vmalloc (sizeof (VObject)); memset (object, 0, sizeof (VObject)); object->name = strdup (argv[1]); object->numPolys = rpoly_count; object->polygon = rpoly; object->order = (unsigned short *) NULL; VComputeObjectExtent (object); stbl[i].type = CT_SURFACE; stbl[i].flags = FL_FIXED_OBJECT; stbl[i].cinfo = newCraft (); if (! stbl[i].cinfo) { fprintf (stderr, "Out of craft types while loading scene.\n"); exit(1); } stbl[i].cinfo->name = strdup (argv[1]); stbl[i].cinfo->object = object; stbl[i].cinfo->placeProc = NULL; stbl[i].curHeading = stbl[i].curPitch = stbl[i].curRoll = 0.0; stbl[i].Sg.x = stbl[i].Sg.y = stbl[i].Sg.z = 0.0; if (++i == MAXSURFACE) { fprintf (stderr, "Only the first %d surface objects will be displayed.\n", MAXSURFACE); i = MAXSURFACE - 1; } } } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * * NAV: NAVAID record * *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ else if (strcmp (argv[0], "NAV") == 0) { /* * Parse Latitude/Longitude */ strcpy (lat_lon, argv[3]); strcat (lat_lon, argv[4]); DISStringToLatLong (lat_lon, &w); w.z = FEETtoMETERS (atoi (argv[5])); freq = strtod (argv[7], (char **) NULL); AddNavaid (argv[1], argv[2], &w, argv[6], freq); } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * * ILS : Instrument Landing System record * *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ else if (strcmp (argv[0], "ILS") == 0) { /* * Parse Latitude/Longitude of localizer transmitter */ strcpy (lat_lon, argv[5]); strcat (lat_lon, argv[6]); DISStringToLatLong (lat_lon, &w); w.z = FEETtoMETERS (atoi (argv[9])); /* * Is Glide Slope transmitter information present? */ if (strcmp (argv[7], "-") != 0) { strcpy (lat_lon, argv[7]); strcat (lat_lon, argv[8]); DISStringToLatLong (lat_lon, &gsw); gsw.z = w.z; } freq = strtod (argv[4], (char **) NULL); loc_width = strtod (argv[11], (char **) NULL); loc_bearing = strtod (argv[12], (char **) NULL); gs_angle = strtod (argv[13], (char **) NULL); AddILS (argv[3], argv[2], &w, &gsw, argv[10], freq, loc_width, loc_bearing, gs_angle); } else if (strcmp (argv[0], "FEATURES") == 0) { done = 1; } if (done == 0) { fgets (line, sizeof (line), f); } } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * * Now the simple terrain/man-made features * *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ while (s = fscanf (f, "%s %s %s %lf %lf", file, lat, lon, &z, &heading) == 5) { depthcue = 1; if (file[0] == '@') { filep = &file[1]; undersample = 1; } else if (file[0] == '+') { filep = &file[1]; depthcue = 0; } else { filep = file; undersample = 0; } stbl[i].type = CT_SURFACE; if ((c = lookupCraft (filep))) { stbl[i].cinfo = c; } else { char *p = strrchr (filep, '.'); f1 = acm_fopen (filep, "r"); stbl[i].cinfo = newCraft (); stbl[i].cinfo->name = strdup (filep); stbl[i].cinfo->object = NULL; if (p != NULL && (strcmp (p, ".dxf") == 0 || strcmp (p, ".DXF") == 0)) { stbl[i].cinfo->object = VReadDepthCueuedDXFObject (f1, depthcue); } else { stbl[i].cinfo->object = VReadDepthCueuedObject (f1, depthcue); } if (!stbl[i].cinfo->object) { fprintf (stderr, "Error reading object %s\n", filep); exit (1); } if (undersample) { undersampleObject (stbl[i].cinfo->object, 3); } fclose (f1); } #ifdef FLAT_WORLD stbl[i].Sg.x = x * NM; stbl[i].Sg.y = y * NM; stbl[i].Sg.z = z; #else strcat (lat, lon); DISStringToLatLong (lat, &stbl[i].w); stbl[i].w.z = FEETtoMETERS (z); DISWorldCoordinatesToGeocentric (&stbl[i].w, (dis_world_coordinates *) & stbl[i].Sg); #endif stbl[i].curHeading = DEGtoRAD (heading); stbl[i].curPitch = stbl[i].curRoll = 0.0; if (++i == MAXSURFACE) { fprintf (stderr, "Only the first %d surface objects will be displayed.\n", MAXSURFACE); fclose (f); return 0; } } fclose (f); return (s == EOF || s == 0) ? 0 : -1; } /* * undersampleObject * * Remove some of the detail in an object to reduce the CPU overhead * of transforming and clipping it. */ void undersampleObject (VObject * obj, int rate) { int i, j, k; k = 0; for (i = 1; i < obj->numPolys; ++i) { for (j = 0; j < obj->polygon[i]->numVtces; j += rate) { obj->polygon[i]->vertex[k++] = obj->polygon[i]->vertex[j]; } obj->polygon[i]->numVtces = k; } } /* * init : Set up the acm server */ int init (const char *runtime_directory) { int i; craft *p; alarm_id_t id; char ground[256]; if (runtime_directory != NULL) { strcpy (_acm_fopen_library_dir, runtime_directory); } polyCount = 32768; poly = (VPolygon **) Vmalloc (sizeof (VPolygon *) * polyCount); cbase = -CLOUD_BASE; ctop = -CLOUD_TOP; /* * VSetDepthCue() must be called before calling VSetVisibility(). */ VSetDepthCue (VAllocColor ("#A6BBCD"), depth_cue_steps); VSetVisibility (visibility); for ((i = 0, p = stbl); i < MAXSURFACE; (++i, ++p)) p->type = CT_FREE; for ((i = 0, p = ptbl); i < MAXPLAYERS; (++i, ++p)) { p->pIndex = i; p->type = CT_FREE; } for ((i = 0, p = mtbl); i < MAXPROJECTILES; (++i, ++p)) { p->pIndex = i; p->type = CT_FREE; } acm_srand ((int) time (0)); HUDColor = VAllocColor ("white"); whiteColor = VAllocColor ("white"); blackColor = VAllocColor ("black"); radarColor = VAllocColor ("#0c0"); HSIMagentaColor = VAllocColor ("magenta"); radarBackgroundColor = VAllocColor ("#094200"); cloudColor = VAllocDepthCueuedColor ("#ccc", 1); buildExplosion (); /* * Set up the scene */ if (readScene (ground, sizeof (ground)) != 0) { fprintf (stderr, "Fatal error\n"); exit (1); } groundColor = VAllocDepthCueuedColor (ground, 1); /* * Compile the aircraft inventory, DIS entity/munition maps */ if (compileAircraftInventory () != 0) { fprintf (stderr, "Fatal error\n"); exit (1); } if (compileEntityMap("object-map.txt", &eo_map_count, &eo_map) != 0) { fprintf (stderr, "Fatal error\n"); exit(1); } if (compileMunitionMap("munition-map.txt", &mun_map_count, &mun_map) != 0) { fprintf (stderr, "Fatal error\n"); exit(1); } /* * Initialize weapons */ initaim9 (); initm61a1 (); initmk82 (); initaim120 (); /* * Set up the radio/navaid check. */ id = addAlarm (5.0, DMECheckAlarm, NULL, NULL); /* * Add the periodic resupply check procedure */ id = addAlarm (RESUPPLY_INTERVAL, resupplyCheck, NULL, NULL); return 0; } void closeScene() { int i; for (i=0; i