Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Scenery.cpp

Go to the documentation of this file.
00001 /*
00002  * Scenery.cpp
00003  *
00004  * Part of Fly! Legacy project
00005  *
00006  * Copyright 2003 Chris Wallace
00007  *
00008  * Fly! Legacy is free software; you can redistribute it and/or modify
00009  *   it under the terms of the GNU General Public License as published by
00010  *   the Free Software Foundation; either version 2 of the License, or
00011  *   (at your option) any later version.
00012  *
00013  * Fly! Legacy is distributed in the hope that it will be useful,
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *   GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  *   along with Fly! Legacy; if not, write to the Free Software
00020  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00031 #include <math.h>
00032 #include "../Include/Utility.h"
00033 #include "../Include/Globals.h"
00034 
00035 
00036 //
00037 // If this macro is defined, then a table of globe tile latitude boundaries is
00038 //   dumped to a text file for debugging immediately after initialization
00039 //
00040 //#define DUMP_GLOBE_TILE_BOUNDARIES
00041 
00042 
00043 //
00044 // Allocate globe tile latitude lookup table
00045 //
00046 static double globe_tile_lat_table [129];
00047 
00061 void init_globe_tile_table ()
00062 {
00063   int i;
00064 
00065   double k = 360.0 / 256.0;
00066   globe_tile_lat_table[0] = 0.0;
00067   for (i=1; i<129; i++) {
00068     double prev = globe_tile_lat_table[i-1];
00069     double prevRad = DegToRad (prev);
00070     globe_tile_lat_table[i] = prev + k * cos(prevRad);
00071   }
00072 
00073 #ifdef DUMP_GLOBE_TILE_BOUNDARIES
00074   // DEBUG : Dump table of globe tile latitude boundaries
00075   FILE* f = fopen ("Debug\\LatitudeTable.txt", "w");
00076   fprintf (f, "GT Table:\n\n");
00077   fprintf (f, "Z-Index     South Boundary      North Boundary\n");
00078   for (i=0; i<256; i++) {
00079     double n, s;
00080     globe_tile_lat_bounds (i, s, n);
00081 
00082     SPosition pos;
00083     pos.lon = pos.alt = 0;
00084 
00085     pos.lat = s * 3600;
00086     char temp[64];
00087     FormatPosition (pos, temp);
00088     char south[64];
00089     memset (south, 0, 64);
00090     strncpy (south, temp, 16);
00091 
00092     pos.lat = n * 3600;
00093     FormatPosition (pos, temp);
00094     char north[64];
00095     memset (north, 0, 64);
00096     strncpy (north, temp, 16);
00097 
00098     double refLat;
00099     if (i >= 128) {
00100       // Northern hemisphere, use southern boundary latitude as reference
00101       refLat = s * 3600;
00102     } else {
00103       // Southern hemisphere, use northern boundary latitude as reference
00104       refLat = n * 3600;
00105     }
00106     fprintf (f, " %3d       %s    %s\n", i, south, north);
00107   }
00108 
00109   // Dump QGT table
00110   fprintf (f, "\n\n");
00111   fprintf (f, "QGT Table:\n");
00112   fprintf (f, "----------\n");
00113   fprintf (f, "Z-Index     South Boundary      North Boundary\n");
00114   for (i=0; i<512; i++) {
00115     double n, s;
00116     qgt_lat_bounds (i, s, n);
00117 
00118     SPosition pos;
00119     pos.lon = pos.alt = 0;
00120 
00121     pos.lat = s * 3600;
00122     char temp[64];
00123     FormatPosition (pos, temp);
00124     char south[64];
00125     memset (south, 0, 64);
00126     strncpy (south, temp, 16);
00127 
00128     pos.lat = n * 3600;
00129     FormatPosition (pos, temp);
00130     char north[64];
00131     memset (north, 0, 64);
00132     strncpy (north, temp, 16);
00133 
00134     double refLat;
00135     if (i >= 128) {
00136       // Northern hemisphere, use southern boundary latitude as reference
00137       refLat = s * 3600;
00138     } else {
00139       // Southern hemisphere, use northern boundary latitude as reference
00140       refLat = n * 3600;
00141     }
00142     fprintf (f, " %3d       %s    %s\n", i, south, north);
00143   }
00144 
00145   fclose (f);
00146 #endif
00147 }
00148 
00149 
00156 static double globe_tile_lon (int x)
00157 {
00158   return (double)x * 360.0 / 256.0;
00159 }
00160 
00171 void qgt_position (SPosition pos, int &qx, int &qz, double &x, double &z)
00172 {
00173   // Get boundaries of the position QGT and the reference QGT
00174   lat_lon_to_qgt (pos.lat, pos.lon, qx, qz);
00175   double w, e, s, n;
00176   qgt_lon_bounds (qx, w, e);
00177   qgt_lat_bounds (qz, s, n);
00178   w *= 3600.0;
00179   e *= 3600.0;
00180   n *= 3600.0;
00181   s *= 3600.0;
00182 
00183   double xSpan = (e - w) / 2.0;
00184   double zSpan = (n - s) / 2.0;
00185   double xMid = w + xSpan;
00186   double zMid = s + zSpan;
00187   x = -(pos.lon - xMid) / xSpan;
00188   z = -(pos.lat - zMid) / zSpan;
00189 }
00190 
00191 void delta_qgt (SPosition pos, int rx, int rz, double &x, double &z)
00192 {
00193   // x,z indices of the position QGT
00194   int px, pz;
00195   qgt_position (pos, px, pz, x, z);
00196   // Adjust deltas by differences in indices
00197   x += (double)(rx - px);
00198   z += (double)(rz - pz);
00199 }
00200 
00213 void lat_lon_to_qgt (double latArg, double lonArg, int &x, int &z)
00214 {
00215   // Convert from arcsec to degrees
00216   latArg /= 3600.0;
00217   lonArg /= 3600.0;
00218 
00219   int gtx = 0, gtz = 0;
00220   lat_lon_to_globe_tile (latArg, lonArg, gtx, gtz);
00221 
00222   // Determine longitude offset, 0 if the position is in the western half of the GT,
00223   //   1 if in the eastern half
00224   int xOffset = 0;
00225   double w, e;
00226   globe_tile_lon_bounds (gtx, w, e);
00227 
00228   if (lonArg > (w + ((e-w)/2.0))) {
00229     // Position is in the eastern half of the globe tile
00230     xOffset = 1;
00231   }
00232 
00233   // Determine latitude offset:
00234   //   0 if position is in the southern half of the GT
00235   //   1 if position is in the northern half of the GT
00236   int zOffset = 0;
00237   double n, s;
00238   globe_tile_lat_bounds (gtz, s, n);
00239   if (latArg > (s + ((n-s)/2))) {
00240     // Position is in the northern half of the globe tile
00241     zOffset = 1;
00242   }
00243 
00244   // Assign QGT indices
00245   x = (gtx * 2) + xOffset;
00246   z = (gtz * 2) + zOffset;
00247 }
00248 
00264 void lat_lon_to_globe_tile (double latArg, double lonArg, int &x, int &z)
00265 {
00266   /*
00267    * Calculate longitude global and detail tile indices
00268    *
00269    * This is the easy calculation.  The first globe tile (index 0) has
00270    *   the Prime Meridian (longitude 0.0) as its western boundary. Each
00271    *   tile is equally spaced and numbered progressively eastward
00272    *   around the globe.
00273    *
00274    * The longitude argument is first normalized so that it represents
00275    *   the number of degrees East of the Prime Meridian, then the longitude
00276    *   globe tile and detail tile are simply calculated by dividing by 256
00277    */
00278 
00279   // Range-check latitude and longitude parameters
00280   double absLat = fabs(latArg);
00281   if ((lonArg < 0.0) || (lonArg > 360.0) || (absLat > globe_tile_lat_table[128])) {
00282     // Fatal error
00283     gtfo ("lat_lon_to_globe_tile : Illegal lat/lon lat=%f lon=%f", latArg, lonArg);
00284   }
00285 
00286   // Calculate longitude index
00287   x = (int)((lonArg / 360.0) * 256.0);
00288 
00289   // Binary search through latitude table for latitude index
00290   int min = 0;
00291   int max = 128;
00292   while ((max - min) > 1) {
00293     int mid = min + ((max - min) / 2);
00294     if (absLat > globe_tile_lat_table[mid]) {
00295       min = mid;
00296     } else {
00297       max = mid;
00298     }
00299   }
00300 
00301   if (latArg > 0.0) {
00302     // Northern hemisphere
00303     z = min + 128;
00304   } else {
00305     // Southern hemisphere
00306     z = 127 - min;
00307   }
00308 }
00309 
00317 void globe_tile_lon_bounds (int x, double &w, double &e)
00318 {
00319   if ((x < 0) || (x > 255)) {
00320     // Fatal error
00321     gtfo ("globe_tile_lon_bounds : Illegal GT index x=%d", x);
00322   } else {
00323     w = globe_tile_lon (x);
00324     e = globe_tile_lon (x+1);
00325   }
00326 }
00327 
00328 
00336 void globe_tile_lat_bounds (int z, double &s, double &n)
00337 {
00338   if (z <= 127) {
00339     // Southern hemisphere
00340     n = -globe_tile_lat_table[127 - z];
00341     s = -globe_tile_lat_table[128 - z];
00342   } else if (z <= 255) {
00343     // Northern hemisphere;
00344     n = globe_tile_lat_table[z - 127];
00345     s = globe_tile_lat_table[z - 128];
00346   } else {
00347     // Fatal error
00348     gtfo ("globe_tile_lat_bounds : Illegal GT index z=%d", z);
00349   }
00350 }
00351 
00359 void qgt_lon_bounds (int x, double &w, double &e)
00360 {
00361   globe_tile_lon_bounds (x/2, w, e);
00362   // Globe tile bounds are valid
00363   double half = w + ((e - w) / 2.0);
00364   if ((x % 2) == 0) {
00365     // This is the western QGT within the GT
00366     e = half;
00367   } else {
00368     // This is the eastern QGT within the GT
00369     w = half;
00370   }
00371 }
00372 
00380 void qgt_lat_bounds (int z, double &s, double &n)
00381 {
00382   globe_tile_lat_bounds (z/2, s, n);
00383   // Globe tile bounds are valid
00384   double half = s + ((n - s) / 2);
00385   if ((z % 2) == 0) {
00386     // This is the southern QGT within the GT
00387     n = half;
00388   } else {
00389     // This is the northern QGT within the GT
00390     s = half;
00391   }
00392 }
00393 
00394 #define ARCSEC2RAD    ((2.0 * PI) / 3600.0)
00395 
00396 //
00397 // Format a position struct into human-readable text
00398 //
00399 void FormatPosition (SPosition pos, char* s)
00400 {
00401   double latSec = pos.lat;
00402   char latHemi;
00403   if (latSec >= 0) {
00404     latHemi = 'N';
00405   } else {
00406     latHemi = 'S';
00407     latSec = fabs(latSec);
00408   }
00409 
00410   double lonSec = pos.lon;
00411   char lonHemi;
00412   if (lonSec >= (180.0 * 3600)) {
00413     lonHemi = 'W';
00414     lonSec = (360 * 3600) - lonSec;
00415   } else {
00416     lonHemi = 'E';
00417   }
00418 
00419   double latDeg = floor (latSec / 3600);
00420   latSec -= (latDeg * 3600);
00421   double latMin = floor (latSec / 60);
00422   latSec -= (latMin * 60);
00423 
00424   double lonDeg = floor (lonSec / 3600);
00425   lonSec -= (lonDeg * 3600);
00426   double lonMin = floor (lonSec / 60);
00427   lonSec -= (lonMin * 60);
00428 
00429   sprintf (s, "%c %2.0f %2.0f'%7.4f\"  %c %2.0f %2.0f'%7.4f\"",
00430     latHemi, latDeg, latMin, latSec, lonHemi, lonDeg, lonMin, lonSec);
00431 }
00432 
00433 
00434 //
00435 // Format Right Ascension/Declination (both in radians) to a human-readable string
00436 //
00437 void FormatRADec (double ra, double dec, char* s)
00438 {
00439   ra = WrapTwoPi (ra) * 24.0 / (2 * SGD_PI);
00440   dec = RadToDeg (dec);
00441 
00442   // Convert RA to hrs/min/sec
00443   double ra_hr, ra_min, ra_sec;
00444   ra_hr = floor (ra);
00445   ra -= ra_hr;
00446   ra_min = floor (ra * 60);
00447   ra -= ra_min / 60;
00448   ra_sec = ra * 3600;
00449 
00450   // Convert Dec to deg/arcmin/arcsec
00451   double dec_deg, dec_min, dec_sec;
00452   dec_deg = floor (dec);
00453   dec -= dec_deg;
00454   dec_min = floor (dec * 60);
00455   dec -= dec_min / 60;
00456   dec_sec = dec * 3600;
00457 
00458   sprintf (s, "%2.0fh %2.0fm %7.3fs  %3.0fd %2.0fm %7.3fs",
00459     ra_hr, ra_min, ra_sec, dec_deg, dec_min, dec_sec);
00460 }
00461 
00462 
00463 //
00464 // Format Sidereal Time (decimal hours) to a human-readable sting
00465 //
00466 void FormatSiderealTime (double st, char *s)
00467 {
00468   double st_hr, st_min, st_sec;
00469   st_hr = floor (st);
00470   st -= st_hr;
00471   st_min = floor (st * 60);
00472   st -= st_min / 60;
00473   st_sec = st * 3600;
00474 
00475   sprintf (s, "%2.0f:%02.0f:%05.3fs", st_hr, st_min, st_sec);
00476 }
00477 
00478 
00479 //
00480 // Convert CIE Yxy triplet to CIE XYZ
00481 //
00482 // Input parameters:
00483 //  Y CIE Yxy luminance Y value   [0..100]
00484 //  x CIE Yxy chrominance x value   [0..1]
00485 //  y CIE Yxy chrominance y value   [0..1]
00486 //
00487 void CIE_Yxy_to_XYZ (const SCIE in, SCIE &out)
00488 {
00489   out.XYZ.X = in.Yxy.x * (in.Yxy.Y / in.Yxy.y);
00490   out.XYZ.Y = in.Yxy.Y;
00491   out.XYZ.Z = (1 - in.Yxy.x - in.Yxy.y) * (in.Yxy.Y / in.Yxy.y);
00492 }
00493 
00494 
00495 //
00496 // Convert CIE XYZ triplet to RGB colour using the D65 white point.
00497 //
00498 void CIE_XYZ_to_RGB_D65 (const SCIE XYZ, sgdVec3 &RGB)
00499 {
00500   // Now multiply XYZ vector by D65 white point matrix
00501   float mWhite[3][3];
00502   mWhite[0][2] =  3.240479;
00503   mWhite[0][1] = -1.537150;
00504   mWhite[0][0] = -0.498535;
00505 
00506   mWhite[1][2] = -0.969256;
00507   mWhite[1][1] =  1.875992;
00508   mWhite[1][0] =  0.041556;
00509 
00510   mWhite[2][2] =  0.055648;
00511   mWhite[2][1] = -0.204043;
00512   mWhite[2][0] =  1.057311;
00513 
00514   RGB[0] = ((mWhite[0][2] * XYZ.XYZ.X) + (mWhite[0][1] * XYZ.XYZ.Y) + (mWhite[0][0] * XYZ.XYZ.Z)) / 100.0;
00515   RGB[1] = ((mWhite[1][2] * XYZ.XYZ.X) + (mWhite[1][1] * XYZ.XYZ.Y) + (mWhite[1][0] * XYZ.XYZ.Z)) / 100.0;
00516   RGB[2] = ((mWhite[2][2] * XYZ.XYZ.X) + (mWhite[2][1] * XYZ.XYZ.Y) + (mWhite[2][0] * XYZ.XYZ.Z)) / 100.0;
00517 }
00518 
00519 
00520 //
00521 // Returns the cartesian coordinate scaling factor for terrain tiles at
00522 //   a particular latitude.  See TerrainModel.doc for details.
00523 //
00524 float TerrainScale (SPosition pos)
00525 {
00526   return cos(DegToRad(pos.lat / 3600.0));
00527 }
00528 
00529 //
00530 // Returns the size, in feet, of a standardized globe tile
00531 //
00532 float GlobeTileDistance (void)
00533 {
00534   return NmToFeet ((360.0 / 256.0) * 60.0);
00535 }
00536 
00537 //
00538 // The following functions convert an SPosition struct into a set of cartesian
00539 //   world coordinates for rendering.  See Terrain.doc for details.
00540 //
00541 // Find cartesian coordinates of a position relative to the center of the
00542 //   given globe tile
00543 //
00544 SVector PosToFlatCartesian (SPosition pos, int x, int z)
00545 {
00546   // Get lat and lon boundaries for this globe tile, convert to arcsec
00547   double s, n, w, e;
00548   globe_tile_lat_bounds (z, s, n);
00549   globe_tile_lon_bounds (x, w, e);
00550   double latScale = (360.0 / 256.0) / fabs(n - s);
00551   n *= 3600;
00552   s *= 3600;
00553   w *= 3600;
00554   e *= 3600;
00555 
00556   // Calculate midpoint lat and lon
00557   double midLon = w + ((e - w) / 2);
00558   double midLat;
00559   if (pos.lat >= 0) {
00560     // Northern hemisphere
00561     midLat = s + ((n - s) / 2);
00562   } else {
00563     // Southern hemisphere
00564     midLat = n + ((s - n) / 2);
00565   }
00566 
00567   // Calculate lon and lat arcsecs from the midpoint, adjusted to universal globe tile
00568   double dLon = pos.lon - midLon;
00569   double dLat = pos.lat - midLat;
00570   dLat *= latScale;
00571 
00572   // Convert to feet using 1 arcmin = 1 nm
00573   double easting = NmToFeet(dLon / 60.0);
00574   double northing = NmToFeet(dLat / 60.0);
00575 
00576   SVector v;
00577   v.x = easting;
00578   v.y = northing;
00579   v.z = pos.alt;
00580 
00581   return v;
00582 }
00583 
00584 SVector PosToFlatCartesian (SPosition pos)
00585 {
00586   int gx, gz;
00587   lat_lon_to_globe_tile (pos.lat/3600.0, pos.lon/3600.0, gx, gz);
00588   return PosToFlatCartesian (pos, gx, gz);
00589 }
00590 
00591 SVector PosToScaledFlatCartesian (SPosition pos)
00592 {
00593   SVector v;
00594 
00595   int gx, gz;
00596   lat_lon_to_globe_tile (pos.lat/3600.0, pos.lon/3600.0, gx, gz);
00597   v = PosToFlatCartesian (pos, gx, gz);
00598 
00599   float scale = TerrainScale (pos);
00600   v.x *= scale;
00601   v.y *= scale;
00602 
00603   return v;
00604 }
00605 
00606 //
00607 // The normalized QGT size is 42.1875 nm square (equivalent to half of 1.40625 deg),
00608 //   so if QGT center coordinate is 0,0 then the bounds are +/- 21.09375 nm.  This
00609 //   macro defines the QGT bound in feet.
00610 //
00611 //#define QGT_FLAT_CARTESIAN_BOUND_FT   (1.28165E+5)
00612 #define QGT_FLAT_CARTESIAN_BOUND_FT   (2.5633E+5)
00613 
00614 
00623 SVector PosToFlatCartesianQgt (SPosition pos, int x, int z)
00624 {
00625   // Get lat and lon boundaries for this QGT, convert to arcsec
00626   double s, n, w, e;
00627   qgt_lat_bounds (z, s, n);
00628   qgt_lon_bounds (x, w, e);
00629   
00630   // Latitude scale adjusts actual QGT size to normalized QGT size
00631   double latScale = (360.0 / 256.0) / (2.0 * fabs(n - s));
00632   
00633   // Convert boundary lat/lon to arcseconds
00634   n *= 3600;
00635   s *= 3600;
00636   w *= 3600;
00637   e *= 3600;
00638 
00639   // Calculate midpoint lat and lon
00640   double midLon = w + ((e - w) / 2.0);
00641   double midLat;
00642   if (pos.lat >= 0) {
00643     // Northern hemisphere
00644     midLat = s + ((n - s) / 2.0);
00645   } else {
00646     // Southern hemisphere
00647     midLat = n + ((s - n) / 2.0);
00648   }
00649 
00650   // Calculate lon and lat arcsecs from the midpoint, adjusted to universal QGT
00651   double dLon = pos.lon - midLon;
00652   double dLat = pos.lat - midLat;
00653   dLat *= latScale;
00654 
00655   // Convert to feet using 1 arcmin = 1 nm
00656   double easting = NmToFeet(dLon / 60.0);
00657   double northing = NmToFeet(dLat / 60.0);
00658 
00659   SVector v;
00660   v.x = easting;
00661   v.y = northing;
00662   v.z = pos.alt;
00663 
00664   return v;
00665 }
00666 
00673 SVector PosToFlatCartesianQgt (SPosition pos)
00674 {
00675   int x, z;
00676   lat_lon_to_qgt (pos.lat, pos.lon, x, z);
00677   return PosToFlatCartesianQgt (pos, x, z);
00678 }
00679 
00686 SVector PosToScaledFlatCartesianQgt (SPosition pos)
00687 {
00688   SVector v = PosToFlatCartesianQgt (pos);
00689 
00690   float scale = TerrainScale (pos);
00691   v.x *= scale;
00692   v.y *= scale;
00693 
00694   return v;
00695 }
SourceForge.net Logo Documentation generated by doxygen