celestiacore.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:147k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // Return true if anything changed that requires re-rendering. Otherwise, we
  2. // can skip rendering, keep the GPU idle, and save power.
  3. bool CelestiaCore::viewUpdateRequired() const
  4. {
  5. #if 1
  6.     // Enable after 1.5.0
  7.     return true;
  8. #else
  9.     bool isPaused = sim->getPauseState() || sim->getTimeScale() == 0.0;
  10.     // See if the camera in any of the views is moving
  11.     bool observersMoving = false;
  12.     for (vector<View*>::const_iterator iter = views.begin(); iter != views.end(); iter++)
  13.     {
  14.         View* v = *iter;
  15.         if (v->observer->getAngularVelocity().length() > 1.0e-10 ||
  16.             v->observer->getVelocity().length() > 1.0e-12)
  17.         {
  18.             observersMoving = true;
  19.             break;
  20.         }
  21.     }
  22.     if (viewChanged ||
  23.         !isPaused ||
  24.         observersMoving ||
  25.         dollyMotion != 0.0 ||
  26.         zoomMotion != 0.0 ||
  27.         scriptState == ScriptRunning ||
  28.         renderer->settingsHaveChanged())
  29.     {
  30.         return true;
  31.     }
  32.     else
  33.     {
  34.         return false;
  35.     }
  36. #endif
  37. }
  38. void CelestiaCore::setViewChanged()
  39. {
  40.     viewChanged = true;
  41. }
  42. void CelestiaCore::splitView(View::Type type, View* av, float splitPos)
  43. {
  44.     setViewChanged();
  45.     if (av == NULL)
  46.         av = (*activeView);
  47.     bool vertical = ( type == View::VerticalSplit );
  48.     Observer* o = sim->addObserver();
  49.     bool tooSmall = false;
  50.     switch (type) // If active view is too small, don't split it.
  51.     {
  52.     case View::HorizontalSplit:
  53.         if (av->height < 0.2f) tooSmall = true;
  54.         break;
  55.     case View::VerticalSplit:
  56.         if (av->width < 0.2f) tooSmall = true;
  57.         break;
  58.     case View::ViewWindow:
  59.         return;
  60.         break;
  61.     }
  62.     if (tooSmall)
  63.     {
  64.         flash(_("View too small to be split"));
  65.         return;
  66.     }
  67.     flash(_("Added view"));
  68.     // Make the new observer a copy of the old one
  69.     // TODO: This works, but an assignment operator for Observer
  70.     // should be defined.
  71.     *o = *(sim->getActiveObserver());
  72.     float w1, h1, w2, h2;
  73.     if (vertical)
  74.     {
  75.         w1 = av->width * splitPos;
  76.         w2 = av->width - w1;
  77.         h1 = av->height;
  78.         h2 = av->height;
  79.     }
  80.     else
  81.     {
  82.         w1 = av->width;
  83.         w2 = av->width;
  84.         h1 = av->height * splitPos;
  85.         h2 = av->height - h1;
  86.     }
  87.     View* split = new View(type,
  88.                            0,
  89.                            av->x,
  90.                            av->y,
  91.                            av->width,
  92.                            av->height);
  93.     split->parent = av->parent;
  94.     if (av->parent != 0)
  95.     {
  96.         if (av->parent->child1 == av)
  97.             av->parent->child1 = split;
  98.         else
  99.             av->parent->child2 = split;
  100.     }
  101.     split->child1 = av;
  102.     av->width = w1;
  103.     av->height = h1;
  104.     av->parent = split;
  105.     View* view = new View(View::ViewWindow,
  106.                           o,
  107.                           av->x + (vertical ? w1 : 0),
  108.                           av->y + (vertical ? 0  : h1),
  109.                           w2, h2);
  110.     split->child2 = view;
  111.     view->parent = split;
  112.     view->zoom = av->zoom;
  113.     views.insert(views.end(), split);
  114.     views.insert(views.end(), view);
  115.     setFOVFromZoom();
  116. }
  117. void CelestiaCore::setFOVFromZoom()
  118. {
  119.     for (list<View*>::iterator i = views.begin(); i != views.end(); i++)
  120.         if ((*i)->type == View::ViewWindow)
  121.         {
  122.             double fov = 2 * atan(height * (*i)->height / (screenDpi / 25.4) / 2. / distanceToScreen) / (*i)->zoom;
  123.             (*i)->observer->setFOV((float) fov);
  124.         }
  125. }
  126. void CelestiaCore::setZoomFromFOV()
  127. {
  128.     for (list<View*>::iterator i = views.begin(); i != views.end(); i++)
  129.         if ((*i)->type == View::ViewWindow)
  130.         {
  131.             (*i)->zoom = (float) (2 * atan(height * (*i)->height / (screenDpi / 25.4) / 2. / distanceToScreen) /  (*i)->observer->getFOV());
  132.         }
  133. }
  134. void CelestiaCore::singleView(View* av)
  135. {
  136.     setViewChanged();
  137.     if (av == NULL)
  138.         av = (*activeView);
  139.     list<View*>::iterator i = views.begin();
  140.     while(i != views.end())
  141.     {
  142.         if ((*i) != av)
  143.         {
  144.             sim->removeObserver((*i)->observer);
  145.             delete (*i)->observer;
  146.             delete (*i);
  147.             i=views.erase(i);
  148.         }
  149.         else
  150.             i++;
  151.     }
  152.     av->x = 0.0f;
  153.     av->y = 0.0f;
  154.     av->width = 1.0f;
  155.     av->height = 1.0f;
  156.     av->parent = 0;
  157.     av->child1 = 0;
  158.     av->child2 = 0;
  159.     activeView = views.begin();
  160.     sim->setActiveObserver((*activeView)->observer);
  161.     setFOVFromZoom();
  162. }
  163. void CelestiaCore::setActiveView(View* v)
  164. {
  165.     activeView = find(views.begin(),views.end(),v);
  166.     sim->setActiveObserver((*activeView)->observer);
  167. }
  168. void CelestiaCore::deleteView(View* v)
  169. {
  170.     if (v == NULL)
  171.         v = (*activeView);
  172.     if (v->parent == 0) return;
  173.     //Erase view and parent view from views
  174.     list<View*>::iterator i = views.begin();
  175.     while(i != views.end())
  176.     {
  177.         if ((*i == v) || (*i == v->parent))
  178.             i=views.erase(i);
  179.         else
  180.             i++;
  181.     }
  182.     int sign;
  183.     View *sibling;
  184.     if (v->parent->child1 == v)
  185.     {
  186.         sibling = v->parent->child2;
  187.         sign = -1;
  188.     }
  189.     else
  190.     {
  191.         sibling = v->parent->child1;
  192.         sign = 1;
  193.     }
  194.     sibling->parent = v->parent->parent;
  195.     if (v->parent->parent != 0) {
  196.         if (v->parent->parent->child1 == v->parent)
  197.             v->parent->parent->child1 = sibling;
  198.         else
  199.             v->parent->parent->child2 = sibling;
  200.     }
  201.     v->walkTreeResize(sibling, sign);
  202.     sim->removeObserver(v->observer);
  203.     delete(v->observer);
  204.     View* nextActiveView = sibling;
  205.     while (nextActiveView->type != View::ViewWindow)
  206.         nextActiveView = nextActiveView->child1;
  207.     activeView = find(views.begin(),views.end(),nextActiveView);
  208.     sim->setActiveObserver((*activeView)->observer);
  209.     delete(v->parent);
  210.     delete(v);
  211.     if (!showActiveViewFrame)
  212.         flashFrameStart = currentTime;
  213.     setFOVFromZoom();
  214. }
  215. bool CelestiaCore::getFramesVisible() const
  216. {
  217.     return showViewFrames;
  218. }
  219. void CelestiaCore::setFramesVisible(bool visible)
  220. {
  221.     setViewChanged();
  222.     showViewFrames = visible;
  223. }
  224. bool CelestiaCore::getActiveFrameVisible() const
  225. {
  226.     return showActiveViewFrame;
  227. }
  228. void CelestiaCore::setActiveFrameVisible(bool visible)
  229. {
  230.     setViewChanged();
  231.     showActiveViewFrame = visible;
  232. }
  233. void CelestiaCore::setContextMenuCallback(ContextMenuFunc callback)
  234. {
  235.     contextMenuCallback = callback;
  236. }
  237. Renderer* CelestiaCore::getRenderer() const
  238. {
  239.     return renderer;
  240. }
  241. Simulation* CelestiaCore::getSimulation() const
  242. {
  243.     return sim;
  244. }
  245. void CelestiaCore::showText(string s,
  246.                             int horig, int vorig,
  247.                             int hoff, int voff,
  248.                             double duration)
  249. {
  250.     messageText = s;
  251.     messageHOrigin = horig;
  252.     messageVOrigin = vorig;
  253.     messageHOffset = hoff;
  254.     messageVOffset = voff;
  255.     messageStart = currentTime;
  256.     messageDuration = duration;
  257. }
  258. int CelestiaCore::getTextWidth(string s) const
  259. {
  260.     return titleFont->getWidth(s);
  261. }
  262. static FormattedNumber SigDigitNum(double v, int digits)
  263. {
  264.     return FormattedNumber(v, digits,
  265.                            FormattedNumber::GroupThousands |
  266.                            FormattedNumber::SignificantDigits);
  267. }
  268. static void displayDistance(Overlay& overlay, double distance)
  269. {
  270.     const char* units = "";
  271.     if (abs(distance) >= astro::parsecsToLightYears(1e+6))
  272.     {
  273.         units = "Mpc";
  274.         distance = astro::lightYearsToParsecs(distance) / 1e+6;
  275.     }
  276.     else if (abs(distance) >= 0.5 * astro::parsecsToLightYears(1e+3))
  277.     {
  278.         units = "Kpc";
  279.         distance = astro::lightYearsToParsecs(distance) / 1e+3;
  280.     }
  281.     else if (abs(distance) >= astro::AUtoLightYears(1000.0f))
  282.     {
  283.         units = _("ly");
  284.     }
  285.     else if (abs(distance) >= astro::kilometersToLightYears(10000000.0))
  286.     {
  287.         units = _("au");
  288.         distance = astro::lightYearsToAU(distance);
  289.     }
  290.     else if (abs(distance) > astro::kilometersToLightYears(1.0f))
  291.     {
  292.         units = "km";
  293.         distance = astro::lightYearsToKilometers(distance);
  294.     }
  295.     else
  296.     {
  297.         units = "m";
  298.         distance = astro::lightYearsToKilometers(distance) * 1000.0f;
  299.     }
  300.     overlay << SigDigitNum(distance, 5) << ' ' << units;
  301. }
  302. static void displayDuration(Overlay& overlay, double days)
  303. {
  304.     if (days > 1.0)
  305.         overlay << FormattedNumber(days, 3, FormattedNumber::GroupThousands) << _(" days");
  306.     else if (days > 1.0 / 24.0)
  307.         overlay << FormattedNumber(days * 24.0, 3, FormattedNumber::GroupThousands) << _(" hours");
  308.     else if (days > 1.0 / (24.0 * 60.0))
  309.         overlay << FormattedNumber(days * 24.0 * 60.0, 3, FormattedNumber::GroupThousands) << _(" minutes");
  310.     else
  311.         overlay << FormattedNumber(days * 24.0 * 60.0 * 60.0, 3, FormattedNumber::GroupThousands) << " seconds";
  312. }
  313. // Display a positive angle as degrees, minutes, and seconds. If the angle is less than one
  314. // degree, only minutes and seconds are shown; if the angle is less than one minute, only
  315. // seconds are displayed.
  316. static void displayAngle(Overlay& overlay, double angle)
  317. {
  318.     int degrees, minutes;
  319.     double seconds;
  320.     astro::decimalToDegMinSec(angle, degrees, minutes, seconds);
  321.     if (degrees > 0)
  322.     {
  323.         overlay.oprintf("%d%s %02d' %.1f"",
  324.                         degrees, UTF8_DEGREE_SIGN, abs(minutes), abs(seconds));
  325.     }
  326.     else if (minutes > 0)
  327.     {
  328.         overlay.oprintf("%02d' %.1f"", abs(minutes), abs(seconds));
  329.     }
  330.     else
  331.     {
  332.         overlay.oprintf("%.2f"", abs(seconds));
  333.     }
  334. }
  335. static void displayDeclination(Overlay& overlay, double angle)
  336. {
  337.     int degrees, minutes;
  338.     double seconds;
  339.     astro::decimalToDegMinSec(angle, degrees, minutes, seconds);
  340.     char sign = '+';
  341.     if (angle < 0.0)
  342.         sign = '-';
  343.     overlay.oprintf("%c%d%s %02d' %.1f"",
  344.                     sign, abs(degrees), UTF8_DEGREE_SIGN, abs(minutes), abs(seconds));
  345. }
  346. static void displayRightAscension(Overlay& overlay, double angle)
  347. {
  348.     int hours, minutes;
  349.     double seconds;
  350.     astro::decimalToHourMinSec(angle, hours, minutes, seconds);
  351.     overlay.oprintf("%dh %02dm %.1fs",
  352.                         hours, abs(minutes), abs(seconds));
  353. }
  354. static void displayApparentDiameter(Overlay& overlay,
  355.                                     double radius,
  356.                                     double distance)
  357. {
  358.     if (distance > radius)
  359.     {
  360.         double arcSize = radToDeg(asin(radius / distance) * 2.0);
  361.         // Only display the arc size if it's less than 160 degrees and greater
  362.         // than one second--otherwise, it's probably not interesting data.
  363.         if (arcSize < 160.0 && arcSize > 1.0 / 3600.0)
  364.         {
  365.             overlay << _("Apparent diameter: ");
  366.             displayAngle(overlay, arcSize);
  367.             overlay << 'n';
  368.         }
  369.     }
  370. }
  371. static void displayApparentMagnitude(Overlay& overlay,
  372.                                      float absMag,
  373.                                      double distance)
  374. {
  375.     float appMag = absMag;
  376.     if (distance > 32.6167)
  377.     {
  378.         appMag = astro::absToAppMag(absMag, (float) distance);
  379.         overlay << _("Apparent magnitude: ");
  380.     }
  381.     else
  382.     {
  383.         overlay << _("Absolute magnitude: ");
  384.     }
  385.     overlay.oprintf("%.1fn", appMag);
  386. }
  387. static void displayRADec(Overlay& overlay, Vec3d v)
  388. {
  389.     double phi = atan2(v.x, v.z) - PI / 2;
  390.     if (phi < 0)
  391.         phi = phi + 2 * PI;
  392.     double theta = atan2(sqrt(v.x * v.x + v.z * v.z), v.y);
  393.     if (theta > 0)
  394.         theta = PI / 2 - theta;
  395.     else
  396.         theta = -PI / 2 - theta;
  397.     double ra = radToDeg(phi);
  398.     double dec = radToDeg(theta);
  399.     overlay << _("RA: ");
  400.     overlay << " ";
  401.     displayRightAscension(overlay, ra);
  402.     overlay << endl;
  403.     overlay << _("Dec: ");
  404.     displayDeclination(overlay, dec);
  405.     overlay << endl;
  406. }
  407. // Display nicely formatted planetocentric/planetographic coordinates.
  408. // The latitude and longitude parameters are angles in radians, altitude
  409. // is in kilometers.
  410. static void displayPlanetocentricCoords(Overlay& overlay,
  411.                                         const Body& body,
  412.                                         double longitude,
  413.                                         double latitude,
  414.                                         double altitude,
  415.                                         bool showAltitude)
  416. {
  417.     char ewHemi = ' ';
  418.     char nsHemi = ' ';
  419.     double lon = 0.0;
  420.     double lat = 0.0;
  421.     // Terrible hack for Earth and Moon longitude conventions.  Fix by
  422.     // adding a field to specify the longitude convention in .ssc files.
  423.     if (body.getName() == "Earth" || body.getName() == "Moon")
  424.     {
  425.         if (latitude < 0.0)
  426.             nsHemi = 'S';
  427.         else if (latitude > 0.0)
  428.             nsHemi = 'N';
  429.         if (longitude < 0.0)
  430.             ewHemi = 'W';
  431.         else if (longitude > 0.0f)
  432.             ewHemi = 'E';
  433.         lon = (float) abs(radToDeg(longitude));
  434.         lat = (float) abs(radToDeg(latitude));
  435.     }
  436.     else
  437.     {
  438.         // Swap hemispheres if the object is a retrograde rotator
  439.         Quatd q = ~body.getEclipticToEquatorial(astro::J2000);
  440.         bool retrograde = (Vec3d(0.0, 1.0, 0.0) * q.toMatrix3()).y < 0.0;
  441.         if ((latitude < 0.0) ^ retrograde)
  442.             nsHemi = 'S';
  443.         else if ((latitude > 0.0) ^ retrograde)
  444.             nsHemi = 'N';
  445.         
  446.         if (retrograde)
  447.             ewHemi = 'E';
  448.         else
  449.             ewHemi = 'W';
  450.         lon = -radToDeg(longitude);
  451.         if (lon < 0.0)
  452.             lon += 360.0;
  453.         lat = abs(radToDeg(latitude));
  454.     }
  455.     overlay.unsetf(ios::fixed);
  456.     overlay << setprecision(6);
  457.     overlay << lat << nsHemi << ' ' << lon << ewHemi;
  458.     if (showAltitude)
  459.         overlay << ' ' << altitude << _("km") << endl;
  460.     overlay << endl;
  461. }
  462. #if 0
  463. // Show the planetocentric latitude, longitude, and altitude of a
  464. // observer.
  465. static void displayObserverPlanetocentricCoords(Overlay& overlay,
  466.                                                 Body& body,
  467.                                                 const UniversalCoord& observerPos,
  468.                                                 double tdb)
  469. {
  470.     // Get the observer position in body-centered ecliptical coordinates
  471.     Vec3d ecl = observerPos - Selection(&body).getPosition(tdb);
  472.     ecl *= astro::microLightYearsToKilometers(1.0);
  473.     Vec3d pc = body.eclipticToPlanetocentric(ecl, tdb);
  474.     displayPlanetocentricCoords(overlay, body, pc.x, pc.y, pc.z, true);
  475. }
  476. #endif
  477. static void displayStarInfo(Overlay& overlay,
  478.                             int detail,
  479.                             Star& star,
  480.                             const Universe& universe,
  481.                             double distance)
  482. {
  483.     overlay << _("Distance: ");
  484.     displayDistance(overlay, distance);
  485.     overlay << 'n';
  486.     if (!star.getVisibility())
  487.     {
  488.         overlay << _("Star system barycentern");
  489.     }
  490.     else
  491.     {
  492.         overlay.oprintf(_("Abs (app) mag: %.2f (%.2f)n"),
  493.                        star.getAbsoluteMagnitude(),
  494.                        astro::absToAppMag(star.getAbsoluteMagnitude(),
  495.                                           (float) distance));
  496.         if (star.getLuminosity() > 1.0e-10f)
  497.             overlay << _("Luminosity: ") << SigDigitNum(star.getLuminosity(), 3) << _("x Sun") << "n";
  498.         overlay << _("Class: ");
  499.         if (star.getSpectralType()[0] == 'Q')
  500.             overlay << _("Neutron star");
  501.         else if (star.getSpectralType()[0] == 'X')
  502.             overlay << _("Black hole");
  503.         else
  504.             overlay << star.getSpectralType();
  505.         overlay << 'n';
  506.         displayApparentDiameter(overlay, star.getRadius(),
  507.                                 astro::lightYearsToKilometers(distance));
  508.         if (detail > 1)
  509.         {
  510.             overlay << _("Surface temp: ") << SigDigitNum(star.getTemperature(), 3) << " Kn";
  511.             float solarRadii = star.getRadius() / 6.96e5f;
  512.             overlay << _("Radius: ");
  513.             if (solarRadii > 0.01f)
  514.             {
  515.                 overlay << SigDigitNum(star.getRadius() / 696000.0f, 2) << " " << _("Rsun")
  516.                         << "  (" << SigDigitNum(star.getRadius(), 3) << " km" << ")n";
  517.             }
  518.             else
  519.             {
  520.                 overlay << SigDigitNum(star.getRadius(), 3) << " kmn";
  521.             }
  522.             if (star.getRotationModel()->isPeriodic())
  523.             {
  524.                 overlay << _("Rotation period: ");
  525.                 float period = (float) star.getRotationModel()->getPeriod();
  526.                 displayDuration(overlay, period);
  527.                 overlay << 'n';
  528.             }
  529.         }
  530.     }
  531.     
  532.     if (detail > 1)
  533.     {
  534.         SolarSystem* sys = universe.getSolarSystem(&star);
  535.         if (sys != NULL && sys->getPlanets()->getSystemSize() != 0)
  536.             overlay << _("Planetary companions presentn");
  537.     }
  538. }
  539. static void displayDSOinfo(Overlay& overlay, const DeepSkyObject& dso, double distance)
  540. {
  541.     char descBuf[128];
  542.     dso.getDescription(descBuf, sizeof(descBuf));
  543.     overlay << descBuf << 'n';
  544.     if (distance >= 0)
  545.     {
  546.      overlay << _("Distance: ");
  547.      displayDistance(overlay, distance);
  548.     }
  549.     else
  550.     {
  551.         overlay << _("Distance from center: ");
  552.         displayDistance(overlay, distance + dso.getRadius());
  553.      }
  554.     overlay << 'n';
  555.     overlay << _("Radius: ");
  556.     displayDistance(overlay, dso.getRadius());
  557.     overlay << 'n';
  558.     displayApparentDiameter(overlay, dso.getRadius(), distance);
  559.     if (dso.getAbsoluteMagnitude() > DSO_DEFAULT_ABS_MAGNITUDE)
  560.     {
  561.         displayApparentMagnitude(overlay,
  562.                                  dso.getAbsoluteMagnitude(),
  563.                                  distance);
  564.     }
  565. }
  566. static void displayPlanetInfo(Overlay& overlay,
  567.                               int detail,
  568.                               Body& body,
  569.                               double t,
  570.                               double distance,
  571.                               Vec3d viewVec)
  572. {
  573.     double kmDistance = astro::lightYearsToKilometers(distance);
  574.     overlay << _("Distance: ");
  575.     distance = astro::kilometersToLightYears(kmDistance - body.getRadius());
  576.     displayDistance(overlay, distance);
  577.     overlay << 'n';
  578.     if (body.getClassification() == Body::Invisible)
  579.     {
  580.         return;
  581.     }
  582.     overlay << _("Radius: ");
  583.     distance = astro::kilometersToLightYears(body.getRadius());
  584.     displayDistance(overlay, distance);
  585.     overlay << 'n';
  586.     displayApparentDiameter(overlay, body.getRadius(), kmDistance);
  587. // Display the phase angle
  588. // Find the parent star of the body. This can be slightly complicated if
  589. // the body orbits a barycenter instead of a star.
  590. Selection parent = Selection(&body).parent();
  591. while (parent.body() != NULL)
  592. parent = parent.parent();
  593. if (parent.star() != NULL)
  594. {
  595. bool showPhaseAngle = false;
  596. Star* sun = parent.star();
  597. if (sun->getVisibility())
  598. {
  599. showPhaseAngle = true;
  600. }
  601. else if (sun->getOrbitingStars())
  602. {
  603. // The planet's orbit is defined with respect to a barycenter. If there's
  604. // a single star orbiting the barycenter, we'll compute the phase angle
  605. // for the planet with respect to that star. If there are no stars, the
  606. // planet is an orphan, drifting through space with no star. We also skip
  607. // displaying the phase angle when there are multiple stars (for now.)
  608. if (sun->getOrbitingStars()->size() == 1)
  609. {
  610. sun = sun->getOrbitingStars()->at(0);
  611. showPhaseAngle = sun->getVisibility();
  612. }
  613. }
  614. if (showPhaseAngle)
  615. {
  616. Vec3d sunVec = Selection(&body).getPosition(t) - Selection(sun).getPosition(t);
  617. sunVec.normalize();
  618. double cosPhaseAngle = sunVec * ((1.0 / viewVec.length()) * viewVec);
  619. double phaseAngle = acos(cosPhaseAngle);
  620. overlay.oprintf("Phase angle: %.1f%sn", radToDeg(phaseAngle), UTF8_DEGREE_SIGN);
  621. }
  622. }
  623.     if (detail > 1)
  624.     {
  625.         if (body.getRotationModel(t)->isPeriodic())
  626.         {
  627.             overlay << _("Rotation period: ");
  628.             displayDuration(overlay, body.getRotationModel(t)->getPeriod());
  629.             overlay << 'n';
  630.         }
  631.         PlanetarySystem* system = body.getSystem();
  632.         if (system != NULL)
  633.         {
  634.             const Star* sun = system->getStar();
  635.             if (sun != NULL)
  636.             {
  637.                 double distFromSun = body.getAstrocentricPosition(t).distanceFromOrigin();
  638.                 float planetTemp = sun->getTemperature() *
  639.                     (float) (::pow(1.0 - body.getAlbedo(), 0.25) *
  640.                              sqrt(sun->getRadius() / (2.0 * distFromSun)));
  641.                 overlay << setprecision(0);
  642.                 overlay << _("Temperature: ") << planetTemp << " Kn";
  643.                 overlay << setprecision(3);
  644.             }
  645. #if 0
  646.             // Code to display apparent magnitude.  Disabled because it's not very
  647.             // accurate.  Too many simplifications are used when computing the amount
  648.             // of light reflected from a body.
  649.             Point3d bodyPos = body.getAstrocentricPosition(t);
  650.             float appMag = body.getApparentMagnitude(*sun,
  651.                                                      bodyPos - Point3d(0, 0, 0),
  652.                                                      viewVec);
  653.             overlay.oprintf(_("Apparent mag: %.2fn"), appMag);
  654. #endif
  655.         }
  656.     }
  657. }
  658. static void displayLocationInfo(Overlay& overlay,
  659.                                 Location& location,
  660.                                 double distance)
  661. {
  662.     overlay << _("Distance: ");
  663.     displayDistance(overlay, distance);
  664.     overlay << 'n';
  665.     Body* body = location.getParentBody();
  666.     if (body != NULL)
  667.     {
  668.         Vec3f locPos = location.getPosition();
  669.         Vec3d lonLatAlt = body->cartesianToPlanetocentric(Vec3d(locPos.x, locPos.y, locPos.z));
  670.         displayPlanetocentricCoords(overlay, *body,
  671.                                     lonLatAlt.x, lonLatAlt.y, lonLatAlt.z, false);
  672.     }
  673. }
  674. static void displaySelectionName(Overlay& overlay,
  675.                                  const Selection& sel,
  676.                                  const Universe& univ)
  677. {
  678.     switch (sel.getType())
  679.     {
  680.     case Selection::Type_Body:
  681.         overlay << sel.body()->getName(true).c_str();
  682.         break;
  683.     case Selection::Type_DeepSky:
  684.         overlay << univ.getDSOCatalog()->getDSOName(sel.deepsky(), true);
  685.         break;
  686.     case Selection::Type_Star:
  687.         //displayStarName(overlay, *(sel.star()), *univ.getStarCatalog());
  688.         overlay << ReplaceGreekLetterAbbr(univ.getStarCatalog()->getStarName(*sel.star(), true));
  689.         break;
  690.     case Selection::Type_Location:
  691.         overlay << sel.location()->getName(true).c_str();
  692.         break;
  693.     default:
  694.         break;
  695.     }
  696. }
  697. static void showViewFrame(const View* v, int width, int height)
  698. {
  699.     glBegin(GL_LINE_LOOP);
  700.     glVertex3f(v->x * width, v->y * height, 0.0f);
  701.     glVertex3f(v->x * width, (v->y + v->height) * height - 1, 0.0f);
  702.     glVertex3f((v->x + v->width) * width - 1, (v->y + v->height) * height - 1, 0.0f);
  703.     glVertex3f((v->x + v->width) * width - 1, v->y * height, 0.0f);
  704.     glEnd();
  705. }
  706. void CelestiaCore::renderOverlay()
  707. {
  708. #ifdef CELX
  709.     if (luaHook) luaHook->callLuaHook(this,"renderoverlay");
  710. #endif
  711.     if (font == NULL)
  712.         return;
  713.     overlay->setFont(font);
  714.     int fontHeight = font->getHeight();
  715.     int emWidth = font->getWidth("M");
  716.     overlay->begin();
  717.     if (views.size() > 1)
  718.     {
  719.         // Render a thin border arround all views
  720.         if (showViewFrames || resizeSplit)
  721.         {
  722.             glLineWidth(1.0f);
  723.             glDisable(GL_TEXTURE_2D);
  724.             glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
  725.             for(list<View*>::iterator i = views.begin(); i != views.end(); i++)
  726.                 if ((*i)->type == View::ViewWindow)
  727.                     showViewFrame(*i, width, height);
  728.         }
  729.         glLineWidth(1.0f);
  730.         // Render a very simple border around the active view
  731.         View* av = (*activeView);
  732.         if (showActiveViewFrame)
  733.         {
  734.             glLineWidth(2.0f);
  735.             glDisable(GL_TEXTURE_2D);
  736.             glColor4f(0.5f, 0.5f, 1.0f, 1.0f);
  737.             showViewFrame(av, width, height);
  738.             glLineWidth(1.0f);
  739.         }
  740.         if (currentTime < flashFrameStart + 0.5)
  741.         {
  742.             glLineWidth(8.0f);
  743.             glColor4f(0.5f, 0.5f, 1.0f,
  744.                       (float) (1.0 - (currentTime - flashFrameStart) / 0.5));
  745.             showViewFrame(av, width, height);
  746.             glLineWidth(1.0f);
  747.         }
  748.     }
  749.     setlocale(LC_NUMERIC, "");
  750.     if (hudDetail > 0 && (overlayElements & ShowTime))
  751.     {
  752.         double lt = 0.0;
  753.  
  754.         if (sim->getSelection().getType() == Selection::Type_Body &&
  755.             (sim->getTargetSpeed() < 0.99 *
  756.              astro::kilometersToMicroLightYears(astro::speedOfLight)))
  757.         {
  758.          if (lightTravelFlag)
  759.          {
  760.              Vec3d v = sim->getSelection().getPosition(sim->getTime()) -
  761.                               sim->getObserver().getPosition();
  762.              // light travel time in days
  763.                 lt = astro::microLightYearsToKilometers(v.length()) / (86400.0 * astro::speedOfLight);
  764.          }
  765.      }
  766.         else
  767.      {
  768.          lt = 0.0;
  769.      }
  770.         double tdb = sim->getTime() + lt;
  771.         astro::Date d = timeZoneBias != 0?astro::TDBtoLocal(tdb):astro::TDBtoUTC(tdb);
  772.         const char* dateStr = d.toCStr(dateFormat);
  773.         int dateWidth = (font->getWidth(dateStr)/(emWidth * 3) + 2) * emWidth * 3;
  774.         if (dateWidth > dateStrWidth) dateStrWidth = dateWidth;
  775.         // Time and date
  776.         glPushMatrix();
  777.         glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
  778.         glTranslatef( (float) (width - dateStrWidth),
  779.                       (float) (height - fontHeight),
  780.                       0.0f);
  781.         overlay->beginText();
  782.         overlay->print(dateStr);
  783.         if (lightTravelFlag && lt > 0.0)
  784.         {
  785.             glColor4f(0.42f, 1.0f, 1.0f, 1.0f);
  786.             *overlay << _("  LT");
  787.             glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
  788.         }
  789.         *overlay << 'n';
  790.         {
  791.             if (abs(abs(sim->getTimeScale()) - 1) < 1e-6)
  792.             {
  793.                 if (sign(sim->getTimeScale()) == 1)
  794.                     *overlay << _("Real time");
  795.                 else
  796.                     *overlay << _("-Real time");
  797.             }
  798.             else if (abs(sim->getTimeScale()) < MinimumTimeRate)
  799.             {
  800.                 *overlay << _("Time stopped");
  801.             }
  802.             else if (abs(sim->getTimeScale()) > 1.0)
  803.             {
  804.                 overlay->oprintf(TIMERATE_PRINTF_FORMAT, sim->getTimeScale());
  805.                 *overlay << UTF8_MULTIPLICATION_SIGN << _(" faster");
  806.             }
  807.             else
  808.             {
  809.                 overlay->oprintf(TIMERATE_PRINTF_FORMAT, 1.0 / sim->getTimeScale());
  810.                 *overlay << UTF8_MULTIPLICATION_SIGN << _(" slower");
  811.             }
  812.             if (sim->getPauseState() == true)
  813.             {
  814.                 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
  815.                 *overlay << _(" (Paused)");
  816.             }
  817.         }
  818.         overlay->endText();
  819.         glPopMatrix();
  820.     }
  821.     if (hudDetail > 0 && (overlayElements & ShowVelocity))
  822.     {
  823.         // Speed
  824.         glPushMatrix();
  825.         glTranslatef(0.0f, (float) (fontHeight * 2 + 5), 0.0f);
  826.         glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
  827.         overlay->beginText();
  828.         *overlay << 'n';
  829.         if (showFPSCounter)
  830.             *overlay << _("FPS: ") << SigDigitNum(fps, 3);
  831.         overlay->setf(ios::fixed);
  832.         *overlay << _("nSpeed: ");
  833.         double speed = sim->getObserver().getVelocity().length();
  834.         if (speed < astro::kilometersToMicroLightYears(1.0f))
  835.             *overlay << SigDigitNum(astro::microLightYearsToKilometers(speed) * 1000.0f, 3) << _(" m/s");
  836.         else if (speed < astro::kilometersToMicroLightYears(10000.0f))
  837.             *overlay << SigDigitNum(astro::microLightYearsToKilometers(speed), 3) << _(" km/s");
  838.         else if (speed < astro::kilometersToMicroLightYears((float) astro::speedOfLight * 100.0f))
  839.             *overlay << SigDigitNum(astro::microLightYearsToKilometers(speed) / astro::speedOfLight, 3) << 'c';
  840.         else if (speed < astro::AUtoMicroLightYears(1000.0f))
  841.             *overlay << SigDigitNum(astro::microLightYearsToAU(speed), 3) << _(" AU/s");
  842.         else
  843.             *overlay << SigDigitNum(speed * 1e-6, 3) << _(" ly/s");
  844.         overlay->endText();
  845.         glPopMatrix();
  846.     }
  847.     if (hudDetail > 0 && (overlayElements & ShowFrame))
  848.     {
  849.         // Field of view and camera mode in lower right corner
  850.         glPushMatrix();
  851.         glTranslatef((float) (width - emWidth * 15),
  852.                      (float) (fontHeight * 3 + 5), 0.0f);
  853.         overlay->beginText();
  854.         glColor4f(0.6f, 0.6f, 1.0f, 1);
  855.         if (sim->getObserverMode() == Observer::Travelling)
  856.         {
  857.             *overlay << _("Travelling ");
  858.             double timeLeft = sim->getArrivalTime() - sim->getRealTime();
  859.             if (timeLeft >= 1)
  860.                 *overlay << '(' << FormattedNumber(timeLeft, 0, FormattedNumber::GroupThousands) << ')';
  861.             *overlay << 'n';
  862.         }
  863.         else
  864.         {
  865.             *overlay << 'n';
  866.         }
  867.         if (!sim->getTrackedObject().empty())
  868.         {
  869.             *overlay << _("Track ");
  870.             displaySelectionName(*overlay, sim->getTrackedObject(),
  871.                                  *sim->getUniverse());
  872.         }
  873.         *overlay << 'n';
  874.         {
  875.             //FrameOfReference frame = sim->getFrame();
  876.             Selection refObject = sim->getFrame()->getRefObject();
  877.             ObserverFrame::CoordinateSystem coordSys = sim->getFrame()->getCoordinateSystem();
  878.             switch (coordSys)
  879.             {
  880.             case ObserverFrame::Ecliptical:
  881.                 *overlay << _("Follow ");
  882.                 displaySelectionName(*overlay, refObject,
  883.                                      *sim->getUniverse());
  884.                 break;
  885.             case ObserverFrame::BodyFixed:
  886.                 *overlay << _("Sync Orbit ");
  887.                 displaySelectionName(*overlay, refObject,
  888.                                      *sim->getUniverse());
  889.                 break;
  890.             case ObserverFrame::PhaseLock:
  891.                 *overlay << _("Lock ");
  892.                 displaySelectionName(*overlay, refObject,
  893.                                      *sim->getUniverse());
  894.                 *overlay << " -> ";
  895.                 displaySelectionName(*overlay, sim->getFrame()->getTargetObject(),
  896.                                      *sim->getUniverse());
  897.                 break;
  898.             case ObserverFrame::Chase:
  899.                 *overlay << _("Chase ");
  900.                 displaySelectionName(*overlay, refObject,
  901.                                      *sim->getUniverse());
  902.                 break;
  903.     default:
  904. break;
  905.             }
  906.             *overlay << 'n';
  907.         }
  908.         glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
  909.         // Field of view
  910.         float fov = radToDeg(sim->getActiveObserver()->getFOV());
  911.         overlay->oprintf(_("FOV: "));
  912.         displayAngle(*overlay, fov);
  913.         overlay->oprintf(" (%.2f%s)n", (*activeView)->zoom,
  914.                         UTF8_MULTIPLICATION_SIGN);
  915.         overlay->endText();
  916.         glPopMatrix();
  917.     }
  918.     // Selection info
  919.     Selection sel = sim->getSelection();
  920.     if (!sel.empty() && hudDetail > 0 && (overlayElements & ShowSelection))
  921.     {
  922.         glPushMatrix();
  923.         glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
  924.         glTranslatef(0.0f, (float) (height - titleFont->getHeight()), 0.0f);
  925.         overlay->beginText();
  926.         Vec3d v = sel.getPosition(sim->getTime()) -
  927.             sim->getObserver().getPosition();
  928.         switch (sel.getType())
  929.         {
  930.         case Selection::Type_Star:
  931.             {
  932.                 if (sel != lastSelection)
  933.                 {
  934.                     lastSelection = sel;
  935.                     selectionNames = sim->getUniverse()->getStarCatalog()->getStarNameList(*sel.star());
  936.                     // Skip displaying the English name if a localized version is present.
  937.                     string starName = sim->getUniverse()->getStarCatalog()->getStarName(*sel.star());
  938.                     string locStarName = sim->getUniverse()->getStarCatalog()->getStarName(*sel.star(), true);
  939.                     if (sel.star()->getCatalogNumber() == 0 && selectionNames.find("Sun") != string::npos && (const char*) "Sun" != _("Sun"))
  940.                     {
  941.                         string::size_type startPos = selectionNames.find("Sun");
  942.                         string::size_type endPos = selectionNames.find(_("Sun"));
  943.                         selectionNames = selectionNames.erase(startPos, endPos - startPos);
  944.                     }
  945.                     else if (selectionNames.find(starName) != string::npos && starName != locStarName)
  946.                     {
  947.                         string::size_type startPos = selectionNames.find(locStarName);
  948.                         selectionNames = selectionNames.substr(startPos);
  949.                     }
  950.                 }
  951.                 overlay->setFont(titleFont);
  952.                 *overlay << selectionNames;
  953.                 overlay->setFont(font);
  954.                 *overlay << 'n';
  955.                 displayStarInfo(*overlay,
  956.                                 hudDetail,
  957.                                 *(sel.star()),
  958.                                 *(sim->getUniverse()),
  959.                                 v.length() * 1e-6);
  960.             }
  961.             break;
  962.         case Selection::Type_DeepSky:
  963.             {
  964.                 if (sel != lastSelection)
  965.                 {
  966.                     lastSelection = sel;
  967.                     selectionNames = sim->getUniverse()->getDSOCatalog()->getDSONameList(sel.deepsky());
  968.                     // Skip displaying the English name if a localized version is present.
  969.                     string DSOName = sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky());
  970.                     string locDSOName = sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky(), true);
  971.                     if (selectionNames.find(DSOName) != string::npos && DSOName != locDSOName)
  972.                     {
  973.                         string::size_type startPos = selectionNames.find(locDSOName);
  974.                         selectionNames = selectionNames.substr(startPos);
  975.                     }
  976.                 }
  977.                 overlay->setFont(titleFont);
  978.                 *overlay << selectionNames;
  979.                 overlay->setFont(font);
  980.                 *overlay << 'n';
  981.                 displayDSOinfo(*overlay, *sel.deepsky(),
  982.                                v.length() * 1e-6 - sel.deepsky()->getRadius());
  983.             }
  984.             break;
  985.         case Selection::Type_Body:
  986.             {
  987.                 // Show all names for the body
  988.                 if (sel != lastSelection)
  989.                 {
  990.                     lastSelection = sel;
  991.                     selectionNames = "";
  992.                     const vector<string>& names = sel.body()->getNames();
  993.                     // Skip displaying the primary name if there's a localized version
  994.                     // of the name.
  995.                     vector<string>::const_iterator firstName = names.begin();
  996.                     if (sel.body()->hasLocalizedName())
  997.                         firstName++;
  998.                     for (vector<string>::const_iterator iter = firstName; iter != names.end(); iter++)
  999.                     {
  1000.                         if (iter != firstName)
  1001.                             selectionNames += " / ";
  1002.                         // Use localized version of parent name in alternative names.
  1003.                         string alias = *iter;
  1004.                         Selection parent = sel.parent();
  1005.                         if (parent.body() != NULL)
  1006.                         {
  1007.                             string parentName = parent.body()->getName();
  1008.                             string locParentName = parent.body()->getName(true);
  1009.                             string::size_type startPos = alias.find(parentName);
  1010.                             if (startPos != string::npos)
  1011.                                 alias.replace(startPos, parentName.length(), locParentName);
  1012.                         }
  1013.                         selectionNames += alias;
  1014.                     }
  1015.                 }
  1016.                 overlay->setFont(titleFont);
  1017.                 *overlay << selectionNames;
  1018.                 overlay->setFont(font);
  1019.                 *overlay << 'n';
  1020.                 displayPlanetInfo(*overlay,
  1021.                                   hudDetail,
  1022.                                   *(sel.body()),
  1023.                                   sim->getTime(),
  1024.                                   v.length() * 1e-6,
  1025.                                   v * astro::microLightYearsToKilometers(1.0));
  1026.             }
  1027.             break;
  1028.         case Selection::Type_Location:
  1029.             overlay->setFont(titleFont);
  1030.             *overlay << sel.location()->getName(true).c_str();
  1031.             overlay->setFont(font);
  1032.             *overlay << 'n';
  1033.             displayLocationInfo(*overlay,
  1034.                                 *(sel.location()),
  1035.                                 v.length() * 1e-6);
  1036.             break;
  1037.       default:
  1038.           break;
  1039.         }
  1040.         
  1041.         // Display RA/Dec for the selection, but only when the observer is near
  1042.         // the Earth.
  1043.         Selection refObject = sim->getFrame()->getRefObject();
  1044.         if (refObject.body() && refObject.body()->getName() == "Earth")
  1045.         {
  1046.             Body* earth = refObject.body();
  1047.             UniversalCoord observerPos = sim->getObserver().getPosition();
  1048.             double distToEarth = (observerPos - refObject.getPosition(sim->getTime())).length();
  1049.             distToEarth = astro::microLightYearsToKilometers(distToEarth) - earth->getRadius();
  1050.             if (distToEarth < 1000.0)
  1051.             {
  1052. #if 1
  1053.                 // Code to show the geocentric RA/Dec
  1054.                 // Only show the coordinates for stars and deep sky objects, where
  1055.                 // the geocentric values will match the apparent values for observers
  1056.                 // near the Earth.
  1057.                 if (sel.star() != NULL || sel.deepsky() != NULL)
  1058.                 {
  1059.                     Vec3d v = sel.getPosition(sim->getTime()) - Selection(earth).getPosition(sim->getTime());
  1060.                     v = v * Mat3d::xrotation(-astro::J2000Obliquity);
  1061.                     displayRADec(*overlay, v);
  1062.                 }
  1063. #else
  1064.                 // Code to display the apparent RA/Dec for the observer
  1065.                 // Don't show RA/Dec for the Earth itself
  1066.                 if (sel.body() != earth)
  1067.                 {
  1068.                     Vec3d vect = sel.getPosition(sim->getTime()) - observerPos;
  1069.                     vect = vect * Mat3d::xrotation(-astro::J2000Obliquity);
  1070.                     displayRADec(*overlay, vect);
  1071.                 }
  1072.                 // Show the geocentric coordinates of the observer, required for
  1073.                 // converting the selection RA/Dec from observer-centric to some
  1074.                 // other coordinate system.
  1075.                 // TODO: We should really show the planetographic (for Earth, geodetic)
  1076.                 // coordinates.
  1077.                 displayObserverPlanetocentricCoords(*overlay,
  1078.                                                     *earth,
  1079.                                                     observerPos,
  1080.                                                     sim->getTime());
  1081. #endif
  1082.             }
  1083.         }
  1084.         overlay->endText();
  1085.         glPopMatrix();
  1086.     }
  1087.     // Text input
  1088.     if (textEnterMode & KbAutoComplete)
  1089.     {
  1090.         overlay->setFont(titleFont);
  1091.         glPushMatrix();
  1092.         glColor4f(0.7f, 0.7f, 1.0f, 0.2f);
  1093.         overlay->rect(0.0f, 0.0f, (float) width, 100.0f);
  1094.         glTranslatef(0.0f, fontHeight * 3.0f + 35.0f, 0.0f);
  1095.         glColor4f(0.6f, 0.6f, 1.0f, 1.0f);
  1096.         overlay->beginText();
  1097.         *overlay << _("Target name: ") << ReplaceGreekLetterAbbr(typedText);
  1098.         overlay->endText();
  1099.         overlay->setFont(font);
  1100.         if (typedTextCompletion.size() >= 1)
  1101.         {
  1102.             int nb_cols = 4;
  1103.             int nb_lines = 3;
  1104.             int start = 0;
  1105.             glTranslatef(3.0f, -font->getHeight() - 3.0f, 0.0f);
  1106.             vector<std::string>::const_iterator iter = typedTextCompletion.begin();
  1107.             if (typedTextCompletionIdx >= nb_cols * nb_lines)
  1108.             {
  1109.                start = (typedTextCompletionIdx / nb_lines + 1 - nb_cols) * nb_lines;
  1110.                iter += start;
  1111.             }
  1112.             for (int i=0; iter < typedTextCompletion.end() && i < nb_cols; i++)
  1113.             {
  1114.                 glPushMatrix();
  1115.                 overlay->beginText();
  1116.                 for (int j = 0; iter < typedTextCompletion.end() && j < nb_lines; iter++, j++)
  1117.                 {
  1118.                     if (i * nb_lines + j == typedTextCompletionIdx - start)
  1119.                         glColor4f(1.0f, 0.6f, 0.6f, 1);
  1120.                     else
  1121.                         glColor4f(0.6f, 0.6f, 1.0f, 1);
  1122.                     *overlay << ReplaceGreekLetterAbbr(*iter) << "n";
  1123.                 }
  1124.                 overlay->endText();
  1125.                 glPopMatrix();
  1126.                 glTranslatef((float) (width/nb_cols), 0.0f, 0.0f);
  1127.            }
  1128.         }
  1129.         glPopMatrix();
  1130.         overlay->setFont(font);
  1131.     }
  1132.     // Text messages
  1133.     if (messageText != "" && currentTime < messageStart + messageDuration)
  1134.     {
  1135.         int emWidth = titleFont->getWidth("M");
  1136.         int fontHeight = titleFont->getHeight();
  1137.         int x = messageHOffset * emWidth;
  1138.         int y = messageVOffset * fontHeight;
  1139.         if (messageHOrigin == 0)
  1140.             x += width / 2;
  1141.         else if (messageHOrigin > 0)
  1142.             x += width;
  1143.         if (messageVOrigin == 0)
  1144.             y += height / 2;
  1145.         else if (messageVOrigin > 0)
  1146.             y += height;
  1147.         else if (messageVOrigin < 0)
  1148.             y -= fontHeight;
  1149.         overlay->setFont(titleFont);
  1150.         glPushMatrix();
  1151.         float alpha = 1.0f;
  1152.         if (currentTime > messageStart + messageDuration - 0.5)
  1153.             alpha = (float) ((messageStart + messageDuration - currentTime) / 0.5);
  1154.         glColor4f(1.0f, 1.0f, 1.0f, alpha);
  1155.         glTranslatef((float) x, (float) y, 0.0f);
  1156.         overlay->beginText();
  1157.         *overlay << _(messageText.c_str());
  1158.         overlay->endText();
  1159.         glPopMatrix();
  1160.         overlay->setFont(font);
  1161.     }
  1162.     if (movieCapture != NULL)
  1163.     {
  1164.         int movieWidth = movieCapture->getWidth();
  1165.         int movieHeight = movieCapture->getHeight();
  1166.         glPushMatrix();
  1167.         glColor4f(1, 0, 0, 1);
  1168.         overlay->rect((float) ((width - movieWidth) / 2 - 1),
  1169.                       (float) ((height - movieHeight) / 2 - 1),
  1170.                       (float) (movieWidth + 1),
  1171.                       (float) (movieHeight + 1), false);
  1172.         glTranslatef((float) ((width - movieWidth) / 2),
  1173.                      (float) ((height + movieHeight) / 2 + 2), 0.0f);
  1174.         *overlay << movieWidth << 'x' << movieHeight << _(" at ") <<
  1175.             movieCapture->getFrameRate() << _(" fps");
  1176.         if (recording)
  1177.             *overlay << _("  Recording");
  1178.         else
  1179.             *overlay << _("  Paused");
  1180.         glPopMatrix();
  1181.         glPushMatrix();
  1182.         glTranslatef((float) ((width + movieWidth) / 2 - emWidth * 5),
  1183.                      (float) ((height + movieHeight) / 2 + 2),
  1184.                      0.0f);
  1185.         float sec = movieCapture->getFrameCount() /
  1186.             movieCapture->getFrameRate();
  1187.         int min = (int) (sec / 60);
  1188.         sec -= min * 60.0f;
  1189.         overlay->oprintf("%3d:%05.2f", min, sec);
  1190.         glPopMatrix();
  1191.         glPushMatrix();
  1192.         glTranslatef((float) ((width - movieWidth) / 2),
  1193.                      (float) ((height - movieHeight) / 2 - fontHeight - 2),
  1194.                      0.0f);
  1195.         *overlay << _("F11 Start/Pause    F12 Stop");
  1196.         glPopMatrix();
  1197.         glPopMatrix();
  1198.     }
  1199.     if (editMode)
  1200.     {
  1201.         glPushMatrix();
  1202.         glTranslatef((float) ((width - font->getWidth(_("Edit Mode"))) / 2),
  1203.                      (float) (height - fontHeight), 0.0f);
  1204.         glColor4f(1, 0, 1, 1);
  1205.         *overlay << _("Edit Mode");
  1206.         glPopMatrix();
  1207.     }
  1208.     // Show logo at start
  1209.     if (logoTexture != NULL)
  1210.     {
  1211.         glEnable(GL_TEXTURE_2D);
  1212.         if (currentTime < 5.0)
  1213.         {
  1214.             int xSize = (int) (logoTexture->getWidth() * 0.8f);
  1215.             int ySize = (int) (logoTexture->getHeight() * 0.8f);
  1216.             int left = (width - xSize) / 2;
  1217.             int bottom = height / 2;
  1218.             float topAlpha, botAlpha;
  1219.             if (currentTime < 4.0)
  1220.             {
  1221.                 botAlpha = (float) clamp(currentTime / 1.0);
  1222.                 topAlpha = (float) clamp(currentTime / 4.0);
  1223.             }
  1224.             else
  1225.             {
  1226.                 botAlpha = topAlpha = (float) (5.0 - currentTime);
  1227.             }
  1228.             logoTexture->bind();
  1229.             glBegin(GL_QUADS);
  1230.             glColor4f(0.8f, 0.8f, 1.0f, botAlpha);
  1231.             //glColor4f(1.0f, 1.0f, 1.0f, botAlpha);
  1232.             glTexCoord2f(0.0f, 1.0f);
  1233.             glVertex2i(left, bottom);
  1234.             glTexCoord2f(1.0f, 1.0f);
  1235.             glVertex2i(left + xSize, bottom);
  1236.             glColor4f(0.6f, 0.6f, 1.0f, topAlpha);
  1237.             //glColor4f(1.0f, 1.0f, 1.0f, topAlpha);
  1238.             glTexCoord2f(1.0f, 0.0f);
  1239.             glVertex2i(left + xSize, bottom + ySize);
  1240.             glTexCoord2f(0.0f, 0.0f);
  1241.             glVertex2i(left, bottom + ySize);
  1242.             glEnd();
  1243.         }
  1244.         else
  1245.         {
  1246.             delete logoTexture;
  1247.             logoTexture = NULL;
  1248.         }
  1249.     }
  1250.     overlay->end();
  1251.     setlocale(LC_NUMERIC, "C");
  1252. }
  1253. class SolarSystemLoader : public EnumFilesHandler
  1254. {
  1255.  public:
  1256.     Universe* universe;
  1257.     ProgressNotifier* notifier;
  1258.     SolarSystemLoader(Universe* u, ProgressNotifier* pn) : universe(u), notifier(pn) {};
  1259.     bool process(const string& filename)
  1260.     {
  1261.         if (DetermineFileType(filename) == Content_CelestiaCatalog)
  1262.         {
  1263.             string fullname = getPath() + '/' + filename;
  1264.             clog << _("Loading solar system catalog: ") << fullname << 'n';
  1265.             if (notifier)
  1266.                 notifier->update(filename);
  1267.             ifstream solarSysFile(fullname.c_str(), ios::in);
  1268.             if (solarSysFile.good())
  1269.             {
  1270.                 LoadSolarSystemObjects(solarSysFile,
  1271.                                        *universe,
  1272.                                        getPath());
  1273.             }
  1274.         }
  1275.         return true;
  1276.     };
  1277. };
  1278. template <class OBJDB> class CatalogLoader : public EnumFilesHandler
  1279. {
  1280. public:
  1281.     OBJDB*      objDB;
  1282.     string      typeDesc;
  1283.     ContentType contentType;
  1284.     ProgressNotifier* notifier;
  1285.     CatalogLoader(OBJDB* db,
  1286.                   const std::string& typeDesc,
  1287.                   const ContentType& contentType,
  1288.                   ProgressNotifier* pn) :
  1289.         objDB      (db),
  1290.         typeDesc   (typeDesc),
  1291.         contentType(contentType),
  1292.         notifier(pn)
  1293.     {
  1294.     }
  1295.     bool process(const string& filename)
  1296.     {
  1297.         if (DetermineFileType(filename) == contentType)
  1298.         {
  1299.             string fullname = getPath() + '/' + filename;
  1300.             clog << _("Loading ") << typeDesc << " catalog: " << fullname << 'n';
  1301.             if (notifier)
  1302.                 notifier->update(filename);
  1303.             ifstream catalogFile(fullname.c_str(), ios::in);
  1304.             if (catalogFile.good())
  1305.             {
  1306.                 bool success = objDB->load(catalogFile, getPath());
  1307.                 if (!success)
  1308.                 {
  1309.                     //DPRINTF(0, _("Error reading star file: %sn"), fullname.c_str());
  1310.                     DPRINTF(0, "Error reading %s catalog file: %sn", typeDesc.c_str(), fullname.c_str());
  1311.                 }
  1312.             }
  1313.         }
  1314.         return true;
  1315.     }
  1316. };
  1317. typedef CatalogLoader<StarDatabase> StarLoader;
  1318. typedef CatalogLoader<DSODatabase>  DeepSkyLoader;
  1319. bool CelestiaCore::initSimulation(const string* configFileName,
  1320.                                   const vector<string>* extrasDirs,
  1321.                                   ProgressNotifier* progressNotifier)
  1322. {
  1323.     // Say we're not ready to render yet.
  1324.     // bReady = false;
  1325. #ifdef REQUIRE_LICENSE_FILE
  1326.     // Check for the presence of the license file--don't run unless it's there.
  1327.     {
  1328.         ifstream license("License.txt");
  1329.         if (!license.good())
  1330.         {
  1331.             fatalError(_("License file 'License.txt' is missing!"));
  1332.             return false;
  1333.         }
  1334.     }
  1335. #endif
  1336.     if (configFileName != NULL)
  1337.     {
  1338.         config = ReadCelestiaConfig(*configFileName);
  1339.     }
  1340.     else
  1341.     {
  1342.         config = ReadCelestiaConfig("celestia.cfg");
  1343.         string localConfigFile = WordExp("~/.celestia.cfg");
  1344.         if (localConfigFile != "")
  1345.             ReadCelestiaConfig(localConfigFile.c_str(), config);
  1346.     }
  1347.     if (config == NULL)
  1348.     {
  1349.         fatalError(_("Error reading configuration file."));
  1350.         return false;
  1351.     }
  1352.     // Set the console log size; ignore any request to use less than 100 lines
  1353.     if (config->consoleLogRows > 100)
  1354.         console.setRowCount(config->consoleLogRows);
  1355. #ifdef USE_SPICE
  1356.     if (!InitializeSpice())
  1357.     {
  1358.         fatalError(_("Initialization of SPICE library failed."));
  1359.         return false;
  1360.     }
  1361. #endif
  1362.     // Insert additional extras directories into the configuration. These
  1363.     // additional directories typically come from the command line. It may
  1364.     // be useful to permit other command line overrides of config file fields.
  1365.     if (extrasDirs != NULL)
  1366.     {
  1367.         // Only insert the additional extras directories that aren't also
  1368.         // listed in the configuration file. The additional directories are added
  1369.         // after the ones from the config file and the order in which they were
  1370.         // specified is preserved. This process in O(N*M), but the number of
  1371.         // additional extras directories should be small.
  1372.         for (vector<string>::const_iterator iter = extrasDirs->begin();
  1373.              iter != extrasDirs->end(); iter++)
  1374.         {
  1375.             if (find(config->extrasDirs.begin(), config->extrasDirs.end(), *iter) ==
  1376.                 config->extrasDirs.end())
  1377.             {
  1378.                 config->extrasDirs.push_back(*iter);
  1379.             }
  1380.         }
  1381.     }
  1382. #ifdef CELX
  1383.     initLuaHook(progressNotifier);
  1384. #endif
  1385.     KeyRotationAccel = degToRad(config->rotateAcceleration);
  1386.     MouseRotationSensitivity = degToRad(config->mouseRotationSensitivity);
  1387.     readFavoritesFile();
  1388.     // If we couldn't read the favorites list from a file, allocate
  1389.     // an empty list.
  1390.     if (favorites == NULL)
  1391.         favorites = new FavoritesList();
  1392.     universe = new Universe();
  1393.     /***** Load star catalogs *****/
  1394.     if (!readStars(*config, progressNotifier))
  1395.     {
  1396.         fatalError(_("Cannot read star database."));
  1397.         return false;
  1398.     }
  1399.     /***** Load the deep sky catalogs *****/
  1400.     DSONameDatabase* dsoNameDB  = new DSONameDatabase;
  1401.     DSODatabase*     dsoDB      = new DSODatabase;
  1402.     dsoDB->setNameDatabase(dsoNameDB);
  1403.     
  1404. // Load first the vector of dsoCatalogFiles in the data directory (deepsky.dsc, globulars.dsc,...):
  1405.  
  1406. for (vector<string>::const_iterator iter = config->dsoCatalogFiles.begin();
  1407.     iter != config->dsoCatalogFiles.end(); iter++)
  1408.     {
  1409.      if (progressNotifier)
  1410.          progressNotifier->update(*iter);
  1411. ifstream dsoFile(iter->c_str(), ios::in);
  1412.         if (!dsoFile.good())
  1413.         {
  1414.          cerr<< _("Error opening deepsky catalog file.") << 'n';
  1415.             delete dsoDB;
  1416.             return false;
  1417. }
  1418.         else if (!dsoDB->load(dsoFile, ""))
  1419.     {
  1420.      cerr << "Cannot read Deep Sky Objects database." << 'n';
  1421.          delete dsoDB;
  1422.             return false;
  1423.         }
  1424.     }
  1425.     // Next, read all the deep sky files in the extras directories
  1426.     {
  1427.         for (vector<string>::const_iterator iter = config->extrasDirs.begin();
  1428.              iter != config->extrasDirs.end(); iter++)
  1429.         {
  1430.             if (*iter != "")
  1431.             {
  1432.                 Directory* dir = OpenDirectory(*iter);
  1433.                 DeepSkyLoader loader(dsoDB,
  1434.                                      "deep sky object",
  1435.                                      Content_CelestiaDeepSkyCatalog,
  1436.                                      progressNotifier);
  1437.                 loader.pushDir(*iter);
  1438.                 dir->enumFiles(loader, true);
  1439.                 delete dir;
  1440.             }
  1441.         }
  1442.     }
  1443.     dsoDB->finish();
  1444.     universe->setDSOCatalog(dsoDB);
  1445.     /***** Load the solar system catalogs *****/
  1446.     // First read the solar system files listed individually in the
  1447.     // config file.
  1448.     {
  1449.         SolarSystemCatalog* solarSystemCatalog = new SolarSystemCatalog();
  1450.         universe->setSolarSystemCatalog(solarSystemCatalog);
  1451.         for (vector<string>::const_iterator iter = config->solarSystemFiles.begin();
  1452.              iter != config->solarSystemFiles.end();
  1453.              iter++)
  1454.         {
  1455.             if (progressNotifier)
  1456.                 progressNotifier->update(*iter);
  1457.             ifstream solarSysFile(iter->c_str(), ios::in);
  1458.             if (!solarSysFile.good())
  1459.             {
  1460.                 warning(_("Error opening solar system catalog.n"));
  1461.             }
  1462.             else
  1463.             {
  1464.                 LoadSolarSystemObjects(solarSysFile, *universe, "");
  1465.             }
  1466.         }
  1467.     }
  1468.     // Next, read all the solar system files in the extras directories
  1469.     {
  1470.         for (vector<string>::const_iterator iter = config->extrasDirs.begin();
  1471.              iter != config->extrasDirs.end(); iter++)
  1472.         {
  1473.             if (*iter != "")
  1474.             {
  1475.                 Directory* dir = OpenDirectory(*iter);
  1476.                 SolarSystemLoader loader(universe, progressNotifier);
  1477.                 loader.pushDir(*iter);
  1478.                 dir->enumFiles(loader, true);
  1479.                 delete dir;
  1480.             }
  1481.         }
  1482.     }
  1483.     // Load asterisms:
  1484.     if (config->asterismsFile != "")
  1485.     {
  1486.         ifstream asterismsFile(config->asterismsFile.c_str(), ios::in);
  1487.         if (!asterismsFile.good())
  1488.         {
  1489.             warning(_("Error opening asterisms file."));
  1490.         }
  1491.         else
  1492.         {
  1493.             AsterismList* asterisms = ReadAsterismList(asterismsFile,
  1494.                                                        *universe->getStarCatalog());
  1495.             universe->setAsterisms(asterisms);
  1496.         }
  1497.     }
  1498.     if (config->boundariesFile != "")
  1499.     {
  1500.         ifstream boundariesFile(config->boundariesFile.c_str(), ios::in);
  1501.         if (!boundariesFile.good())
  1502.         {
  1503.             warning(_("Error opening constellation boundaries files."));
  1504.         }
  1505.         else
  1506.         {
  1507.             ConstellationBoundaries* boundaries = ReadBoundaries(boundariesFile);
  1508.             universe->setBoundaries(boundaries);
  1509.         }
  1510.     }
  1511.     // Load destinations list
  1512.     if (config->destinationsFile != "")
  1513.     {
  1514.         string localeDestinationsFile = LocaleFilename(config->destinationsFile);
  1515.         ifstream destfile(localeDestinationsFile.c_str());
  1516.         if (destfile.good())
  1517.         {
  1518.             destinations = ReadDestinationList(destfile);
  1519.         }
  1520.     }
  1521.     sim = new Simulation(universe);
  1522.     if((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0)
  1523.     sim->setFaintestVisible(config->faintestVisible);
  1524.     View* view = new View(View::ViewWindow, sim->getActiveObserver(), 0.0f, 0.0f, 1.0f, 1.0f);
  1525.     views.insert(views.end(), view);
  1526.     activeView = views.begin();
  1527.     if (!compareIgnoringCase(getConfig()->cursor, "inverting crosshair"))
  1528.     {
  1529.         defaultCursorShape = CelestiaCore::InvertedCrossCursor;
  1530.     }
  1531.     if (!compareIgnoringCase(getConfig()->cursor, "arrow"))
  1532.     {
  1533.         defaultCursorShape = CelestiaCore::ArrowCursor;
  1534.     }
  1535.     if (cursorHandler != NULL)
  1536.     {
  1537.         cursorHandler->setCursorShape(defaultCursorShape);
  1538.     }
  1539.     return true;
  1540. }
  1541. bool CelestiaCore::initRenderer()
  1542. {
  1543.     renderer->setRenderFlags(Renderer::ShowStars |
  1544.                              Renderer::ShowPlanets |
  1545.                              Renderer::ShowAtmospheres |
  1546.                              Renderer::ShowAutoMag);
  1547.     GLContext* context = new GLContext();
  1548.     assert(context != NULL);
  1549.     if (context == NULL)
  1550.         return false;
  1551.     context->init(config->ignoreGLExtensions);
  1552.     // Choose the render path, starting with the least desirable
  1553.     context->setRenderPath(GLContext::GLPath_Basic);
  1554.     context->setRenderPath(GLContext::GLPath_Multitexture);
  1555.     context->setRenderPath(GLContext::GLPath_DOT3_ARBVP);
  1556.     context->setRenderPath(GLContext::GLPath_NvCombiner_NvVP);
  1557.     context->setRenderPath(GLContext::GLPath_NvCombiner_ARBVP);
  1558.     context->setRenderPath(GLContext::GLPath_GLSL);
  1559.     cout << _("render path: ") << context->getRenderPath() << 'n';
  1560.     Renderer::DetailOptions detailOptions;
  1561.     detailOptions.ringSystemSections = config->ringSystemSections;
  1562.     detailOptions.orbitPathSamplePoints = config->orbitPathSamplePoints;
  1563.     detailOptions.shadowTextureSize = config->shadowTextureSize;
  1564.     detailOptions.eclipseTextureSize = config->eclipseTextureSize;
  1565.     // Prepare the scene for rendering.
  1566.     if (!renderer->init(context, (int) width, (int) height, detailOptions))
  1567.     {
  1568.         fatalError(_("Failed to initialize renderer"));
  1569.         return false;
  1570.     }
  1571.     if ((renderer->getRenderFlags() & Renderer::ShowAutoMag) != 0)
  1572.     {
  1573.         renderer->setFaintestAM45deg(renderer->getFaintestAM45deg());
  1574.         setFaintestAutoMag();
  1575.     }
  1576.     if (config->mainFont == "")
  1577.         font = LoadTextureFont("fonts/default.txf");
  1578.     else
  1579.         font = LoadTextureFont(string("fonts/") + config->mainFont);
  1580.     if (font == NULL)
  1581.     {
  1582.         cout << _("Error loading font; text will not be visible.n");
  1583.     }
  1584.     else
  1585.     {
  1586.         font->buildTexture();
  1587.     }
  1588.     if (config->titleFont != "")
  1589.         titleFont = LoadTextureFont(string("fonts") + "/" + config->titleFont);
  1590.     if (titleFont != NULL)
  1591.         titleFont->buildTexture();
  1592.     else
  1593.         titleFont = font;
  1594.     // Set up the overlay
  1595.     overlay = new Overlay();
  1596.     overlay->setWindowSize(width, height);
  1597.     if (config->labelFont == "")
  1598.     {
  1599.         renderer->setFont(Renderer::FontNormal, font);
  1600.     }
  1601.     else
  1602.     {
  1603.         TextureFont* labelFont = LoadTextureFont(string("fonts") + "/" + config->labelFont);
  1604.         if (labelFont == NULL)
  1605.         {
  1606.             renderer->setFont(Renderer::FontNormal, font);
  1607.         }
  1608.         else
  1609.         {
  1610.             labelFont->buildTexture();
  1611.             renderer->setFont(Renderer::FontNormal, labelFont);
  1612.         }
  1613.     }
  1614.     renderer->setFont(Renderer::FontLarge, titleFont);
  1615.     if (config->logoTextureFile != "")
  1616.     {
  1617.         logoTexture = LoadTextureFromFile(string("textures") + "/" + config->logoTextureFile);
  1618.     }
  1619.     return true;
  1620. }
  1621. static void loadCrossIndex(StarDatabase* starDB,
  1622.                            StarDatabase::Catalog catalog,
  1623.                            const string& filename)
  1624. {
  1625.     if (!filename.empty())
  1626.     {
  1627.         ifstream xrefFile(filename.c_str(), ios::in | ios::binary);
  1628.         if (xrefFile.good())
  1629.         {
  1630.             if (!starDB->loadCrossIndex(catalog, xrefFile))
  1631.                 cerr << _("Error reading cross index ") << filename << 'n';
  1632.             else
  1633.                 clog << _("Loaded cross index ") << filename << 'n';
  1634.         }
  1635.     }
  1636. }
  1637. bool CelestiaCore::readStars(const CelestiaConfig& cfg,
  1638.                              ProgressNotifier* progressNotifier)
  1639. {
  1640.     StarDetails::SetStarTextures(cfg.starTextures);
  1641.     ifstream starNamesFile(cfg.starNamesFile.c_str(), ios::in);
  1642.     if (!starNamesFile.good())
  1643.     {
  1644. cerr << _("Error opening ") << cfg.starNamesFile << 'n';
  1645.         return false;
  1646.     }
  1647.     StarNameDatabase* starNameDB = StarNameDatabase::readNames(starNamesFile);
  1648.     if (starNameDB == NULL)
  1649.     {
  1650.         cerr << _("Error reading star names filen");
  1651.         return false;
  1652.     }
  1653.     // First load the binary star database file.  The majority of stars
  1654.     // will be defined here.
  1655.     StarDatabase* starDB = new StarDatabase();
  1656.     if (!cfg.starDatabaseFile.empty())
  1657.     {
  1658.         if (progressNotifier)
  1659.             progressNotifier->update(cfg.starDatabaseFile);
  1660.         ifstream starFile(cfg.starDatabaseFile.c_str(), ios::in | ios::binary);
  1661.         if (!starFile.good())
  1662.         {
  1663.             cerr << _("Error opening ") << cfg.starDatabaseFile << 'n';
  1664.             delete starDB;
  1665.             return false;
  1666.         }
  1667.         if (!starDB->loadBinary(starFile))
  1668.         {
  1669.             delete starDB;
  1670.             cerr << _("Error reading stars filen");
  1671.             return false;
  1672.         }
  1673.     }
  1674.     starDB->setNameDatabase(starNameDB);
  1675.     loadCrossIndex(starDB, StarDatabase::HenryDraper, cfg.HDCrossIndexFile);
  1676.     loadCrossIndex(starDB, StarDatabase::SAO,         cfg.SAOCrossIndexFile);
  1677.     loadCrossIndex(starDB, StarDatabase::Gliese,      cfg.GlieseCrossIndexFile);
  1678.     // Next, read any ASCII star catalog files specified in the StarCatalogs
  1679.     // list.
  1680.     if (!cfg.starCatalogFiles.empty())
  1681.     {
  1682.         for (vector<string>::const_iterator iter = config->starCatalogFiles.begin();
  1683.              iter != config->starCatalogFiles.end(); iter++)
  1684.         {
  1685.             if (*iter != "")
  1686.             {
  1687.                 ifstream starFile(iter->c_str(), ios::in);
  1688.                 if (starFile.good())
  1689.                 {
  1690.                     starDB->load(starFile, "");
  1691.                 }
  1692.                 else
  1693.                 {
  1694.                     cerr << _("Error opening star catalog ") << *iter << 'n';
  1695.                 }
  1696.             }
  1697.         }
  1698.     }
  1699.     // Now, read supplemental star files from the extras directories
  1700.     for (vector<string>::const_iterator iter = config->extrasDirs.begin();
  1701.          iter != config->extrasDirs.end(); iter++)
  1702.     {
  1703.         if (*iter != "")
  1704.         {
  1705.             Directory* dir = OpenDirectory(*iter);
  1706.             StarLoader loader(starDB, "star", Content_CelestiaStarCatalog, progressNotifier);
  1707.             loader.pushDir(*iter);
  1708.             dir->enumFiles(loader, true);
  1709.             delete dir;
  1710.         }
  1711.     }
  1712.     starDB->finish();
  1713.     universe->setStarCatalog(starDB);
  1714.     return true;
  1715. }
  1716. /// Set the faintest visible star magnitude; adjust the renderer's
  1717. /// brightness parameters appropriately.
  1718. void CelestiaCore::setFaintest(float magnitude)
  1719. {
  1720.     sim->setFaintestVisible(magnitude);
  1721. }
  1722. /// Set faintest visible star magnitude and saturation magnitude
  1723. /// for a given field of view;
  1724. /// adjust the renderer's brightness parameters appropriately.
  1725. void CelestiaCore::setFaintestAutoMag()
  1726. {
  1727.     float faintestMag;
  1728.     renderer->autoMag(faintestMag);
  1729.     sim->setFaintestVisible(faintestMag);
  1730. }
  1731. void CelestiaCore::fatalError(const string& msg)
  1732. {
  1733.     if (alerter == NULL)
  1734.         cout << msg;
  1735.     else
  1736.         alerter->fatalError(msg);
  1737. }
  1738. void CelestiaCore::setAlerter(Alerter* a)
  1739. {
  1740.     alerter = a;
  1741. }
  1742. CelestiaCore::Alerter* CelestiaCore::getAlerter() const
  1743. {
  1744.     return alerter;
  1745. }
  1746. /// Sets the cursor handler object.
  1747. /// This must be set before calling initSimulation
  1748. /// or the default cursor will not be used.
  1749. void CelestiaCore::setCursorHandler(CursorHandler* handler)
  1750. {
  1751.     cursorHandler = handler;
  1752. }
  1753. CelestiaCore::CursorHandler* CelestiaCore::getCursorHandler() const
  1754. {
  1755.     return cursorHandler;
  1756. }
  1757. int CelestiaCore::getTimeZoneBias() const
  1758. {
  1759.     return timeZoneBias;
  1760. }
  1761. bool CelestiaCore::getLightDelayActive() const
  1762. {
  1763.     return lightTravelFlag;
  1764. }
  1765. void CelestiaCore::setLightDelayActive(bool lightDelayActive)
  1766. {
  1767.     lightTravelFlag = lightDelayActive;
  1768. }
  1769. void CelestiaCore::setTextEnterMode(int mode)
  1770. {
  1771.     if (mode != textEnterMode)
  1772.     {
  1773.         if ((mode & KbAutoComplete) != (textEnterMode & KbAutoComplete))
  1774.         {
  1775.             typedText = "";
  1776.             typedTextCompletion.clear();
  1777.             typedTextCompletionIdx = -1;
  1778.         }
  1779.         textEnterMode = mode;
  1780.         notifyWatchers(TextEnterModeChanged);
  1781.     }
  1782. }
  1783. int CelestiaCore::getTextEnterMode() const
  1784. {
  1785.     return textEnterMode;
  1786. }
  1787. void CelestiaCore::setScreenDpi(int dpi)
  1788. {
  1789.     screenDpi = dpi;
  1790.     setFOVFromZoom();
  1791.     renderer->setScreenDpi(dpi);
  1792. }
  1793. int CelestiaCore::getScreenDpi() const
  1794. {
  1795.     return screenDpi;
  1796. }
  1797. void CelestiaCore::setDistanceToScreen(int dts)
  1798. {
  1799.     distanceToScreen = dts;
  1800.     setFOVFromZoom();
  1801. }
  1802. int CelestiaCore::getDistanceToScreen() const
  1803. {
  1804.     return distanceToScreen;
  1805. }
  1806. void CelestiaCore::setTimeZoneBias(int bias)
  1807. {
  1808.     timeZoneBias = bias;
  1809.     notifyWatchers(TimeZoneChanged);
  1810. }
  1811. string CelestiaCore::getTimeZoneName() const
  1812. {
  1813.     return timeZoneName;
  1814. }
  1815. void CelestiaCore::setTimeZoneName(const string& zone)
  1816. {
  1817.     timeZoneName = zone;
  1818. }
  1819. int CelestiaCore::getHudDetail()
  1820. {
  1821.     return hudDetail;
  1822. }
  1823. void CelestiaCore::setHudDetail(int newHudDetail)
  1824. {
  1825.     hudDetail = newHudDetail%3;
  1826.     notifyWatchers(VerbosityLevelChanged);
  1827. }
  1828. astro::Date::Format CelestiaCore::getDateFormat() const
  1829. {
  1830.     return dateFormat;
  1831. }
  1832. void CelestiaCore::setDateFormat(astro::Date::Format format)
  1833. {
  1834.     dateStrWidth = 0;
  1835.     dateFormat = format;
  1836. }
  1837. int CelestiaCore::getOverlayElements() const
  1838. {
  1839.     return overlayElements;
  1840. }
  1841. void CelestiaCore::setOverlayElements(int _overlayElements)
  1842. {
  1843.     overlayElements = _overlayElements;
  1844. }
  1845. void CelestiaCore::initMovieCapture(MovieCapture* mc)
  1846. {
  1847.     if (movieCapture == NULL)
  1848.         movieCapture = mc;
  1849. }
  1850. void CelestiaCore::recordBegin()
  1851. {
  1852.     if (movieCapture != NULL) {
  1853.         recording = true;
  1854.         movieCapture->recordingStatus(true);
  1855.     }
  1856. }
  1857. void CelestiaCore::recordPause()
  1858. {
  1859.     recording = false;
  1860.     if (movieCapture != NULL) movieCapture->recordingStatus(false);
  1861. }
  1862. void CelestiaCore::recordEnd()
  1863. {
  1864.     if (movieCapture != NULL)
  1865.     {
  1866.         recordPause();
  1867.         movieCapture->end();
  1868.         delete movieCapture;
  1869.         movieCapture = NULL;
  1870.     }
  1871. }
  1872. bool CelestiaCore::isCaptureActive()
  1873. {
  1874.     return movieCapture != NULL;
  1875. }
  1876. bool CelestiaCore::isRecording()
  1877. {
  1878.     return recording;
  1879. }
  1880. void CelestiaCore::flash(const string& s, double duration)
  1881. {
  1882.     if (hudDetail > 0)
  1883.         showText(s, -1, -1, 0, 5, duration);
  1884. }
  1885. CelestiaConfig* CelestiaCore::getConfig() const
  1886. {
  1887.     return config;
  1888. }
  1889. void CelestiaCore::addWatcher(CelestiaWatcher* watcher)
  1890. {
  1891.     assert(watcher != NULL);
  1892.     watchers.insert(watchers.end(), watcher);
  1893. }
  1894. void CelestiaCore::removeWatcher(CelestiaWatcher* watcher)
  1895. {
  1896.     vector<CelestiaWatcher*>::iterator iter =
  1897.         find(watchers.begin(), watchers.end(), watcher);
  1898.     if (iter != watchers.end())
  1899.         watchers.erase(iter);
  1900. }
  1901. void CelestiaCore::notifyWatchers(int property)
  1902. {
  1903.     for (vector<CelestiaWatcher*>::iterator iter = watchers.begin();
  1904.          iter != watchers.end(); iter++)
  1905.     {
  1906.         (*iter)->notifyChange(this, property);
  1907.     }
  1908. }
  1909. void CelestiaCore::goToUrl(const string& urlStr)
  1910. {
  1911.     Url url(urlStr, this);
  1912.     url.goTo();
  1913.     notifyWatchers(RenderFlagsChanged | LabelFlagsChanged);
  1914. }
  1915. void CelestiaCore::addToHistory()
  1916. {
  1917.     Url url(this);
  1918.     if (!history.empty() && historyCurrent < history.size() - 1)
  1919.     {
  1920.         // truncating history to current position
  1921.         while (historyCurrent != history.size() - 1)
  1922.         {
  1923.             history.pop_back();
  1924.         }
  1925.     }
  1926.     history.push_back(url);
  1927.     historyCurrent = history.size() - 1;
  1928.     notifyWatchers(HistoryChanged);
  1929. }
  1930. void CelestiaCore::back()
  1931. {
  1932.     if (historyCurrent == 0) return;
  1933.     if (historyCurrent == history.size() - 1)
  1934.     {
  1935.         addToHistory();
  1936.         historyCurrent = history.size()-1;
  1937.     }
  1938.     historyCurrent--;
  1939.     history[historyCurrent].goTo();
  1940.     notifyWatchers(HistoryChanged|RenderFlagsChanged|LabelFlagsChanged);
  1941. }
  1942. void CelestiaCore::forward()
  1943. {
  1944.     if (history.size() == 0) return;
  1945.     if (historyCurrent == history.size()-1) return;
  1946.     historyCurrent++;
  1947.     history[historyCurrent].goTo();
  1948.     notifyWatchers(HistoryChanged|RenderFlagsChanged|LabelFlagsChanged);
  1949. }
  1950. const vector<Url>& CelestiaCore::getHistory() const
  1951. {
  1952.     return history;
  1953. }
  1954. vector<Url>::size_type CelestiaCore::getHistoryCurrent() const
  1955. {
  1956.     return historyCurrent;
  1957. }
  1958. void CelestiaCore::setHistoryCurrent(vector<Url>::size_type curr)
  1959. {
  1960.     if (curr >= history.size()) return;
  1961.     if (historyCurrent == history.size()) {
  1962.         addToHistory();
  1963.     }
  1964.     historyCurrent = curr;
  1965.     history[curr].goTo();
  1966.     notifyWatchers(HistoryChanged|RenderFlagsChanged|LabelFlagsChanged);
  1967. }
  1968. /*! Toggle the specified reference mark for a selection.
  1969.  *  a selection. The default value for the selection argument is
  1970.  *  the current simulation selection. This method does nothing
  1971.  *  if the selection isn't a solar system body.
  1972.  */
  1973. void CelestiaCore::toggleReferenceMark(const string& refMark, Selection sel)
  1974. {
  1975.     Body* body = NULL;
  1976.     if (sel.empty())
  1977.         body = getSimulation()->getSelection().body();
  1978.     else
  1979.         body = sel.body();
  1980.     
  1981.     // Reference marks can only be set for solar system bodies.
  1982.     if (body == NULL)
  1983.         return;
  1984.     if (body->findReferenceMark(refMark))
  1985.     {
  1986.         body->removeReferenceMark(refMark);
  1987.     }
  1988.     else
  1989.     {
  1990.         if (refMark == "body axes")
  1991.         {
  1992.             body->addReferenceMark(new BodyAxisArrows(*body));
  1993.         }
  1994.         else if (refMark == "frame axes")
  1995.         {
  1996.             body->addReferenceMark(new FrameAxisArrows(*body));
  1997.         }
  1998.         else if (refMark == "sun direction")
  1999.         {
  2000.             body->addReferenceMark(new SunDirectionArrow(*body));
  2001.         }
  2002.         else if (refMark == "velocity vector")
  2003.         {
  2004.             body->addReferenceMark(new VelocityVectorArrow(*body));
  2005.         }
  2006.         else if (refMark == "spin vector")
  2007.         {
  2008.             body->addReferenceMark(new SpinVectorArrow(*body));
  2009.         }
  2010.         else if (refMark == "frame center direction")
  2011.         {
  2012.             double now = getSimulation()->getTime();
  2013.             BodyToBodyDirectionArrow* arrow = new BodyToBodyDirectionArrow(*body, body->getOrbitFrame(now)->getCenter());
  2014.             arrow->setTag(refMark);
  2015.             body->addReferenceMark(arrow);
  2016.         }
  2017.         else if (refMark == "planetographic grid")
  2018.         {
  2019.             body->addReferenceMark(new PlanetographicGrid(*body));
  2020.         }
  2021.         else if (refMark == "terminator")
  2022.         {
  2023.             double now = getSimulation()->getTime();
  2024.             Star* sun = NULL;
  2025.             Body* b = body;
  2026.             while (b != NULL)
  2027.             {
  2028.                 Selection center = b->getOrbitFrame(now)->getCenter();
  2029.                 if (center.star() != NULL)
  2030.                     sun = center.star();
  2031.                 b = center.body();
  2032.             }
  2033.             if (sun != NULL)
  2034.             {
  2035.                 VisibleRegion* visibleRegion = new VisibleRegion(*body, Selection(sun));
  2036.                 visibleRegion->setTag("terminator");
  2037.                 body->addReferenceMark(visibleRegion);
  2038.             }
  2039.         }
  2040.     }
  2041. }
  2042. /*! Return whether the specified reference mark is enabled for a
  2043.  *  a selection. The default value for the selection argument is
  2044.  *  the current simulation selection.
  2045.  */
  2046. bool CelestiaCore::referenceMarkEnabled(const string& refMark, Selection sel) const
  2047. {
  2048.     Body* body = NULL;
  2049.     if (sel.empty())
  2050.         body = getSimulation()->getSelection().body();
  2051.     else
  2052.         body = sel.body();
  2053.     
  2054.     // Reference marks can only be set for solar system bodies.
  2055.     if (body == NULL)
  2056.         return false;
  2057.     else
  2058.         return body->findReferenceMark(refMark) != NULL;
  2059. }
  2060. #ifdef CELX
  2061. class LuaPathFinder : public EnumFilesHandler
  2062. {
  2063.  public:
  2064.     string luaPath;
  2065.     LuaPathFinder(string s) : luaPath(s) {};
  2066.     string lastPath;
  2067.     bool process(const string& filename)
  2068.     {
  2069.         if (getPath() != lastPath)
  2070.         {
  2071.             int extPos = filename.rfind('.');
  2072.             if (extPos != (int)string::npos)
  2073.             {
  2074.                 string ext = string(filename, extPos, filename.length() - extPos + 1);
  2075.                 if (ext == ".lua")
  2076.                 {
  2077.                     lastPath = getPath();
  2078.                     string newPatt = getPath()+"/?.lua;";
  2079.                     extPos = luaPath.rfind(newPatt);
  2080.                     if (extPos < 0)
  2081.                     {
  2082.                         luaPath = luaPath + newPatt;
  2083.                     }
  2084.                 }
  2085.             }
  2086.     }
  2087.         return true;
  2088.     };
  2089. };
  2090. // Initialize the Lua hook table as well as the Lua state for scripted
  2091. // objects. The Lua hook operates in a different Lua state than user loaded
  2092. // scripts. It always has file system access via the IO package. If the script
  2093. // system access policy is "allow", then scripted objects will run in the same
  2094. // Lua context as the Lua hook. Sharing state between scripted objects and the
  2095. // hook can be very useful, but it gives system access to scripted objects,
  2096. // and therefore must be restricted based on the system access policy.
  2097. bool CelestiaCore::initLuaHook(ProgressNotifier* progressNotifier)
  2098. {
  2099.     luaHook = new LuaState();
  2100.     luaHook->init(this);
  2101.     string LuaPath = "?.lua;celxx/?.lua;";
  2102.     // Find the path for lua files in the extras directories
  2103.     {
  2104.         for (vector<string>::const_iterator iter = config->extrasDirs.begin();
  2105.              iter != config->extrasDirs.end(); iter++)
  2106.         {
  2107.             if (*iter != "")
  2108.             {
  2109.                 Directory* dir = OpenDirectory(*iter);
  2110.                 LuaPathFinder loader("");
  2111.                 loader.pushDir(*iter);
  2112.                 dir->enumFiles(loader, true);
  2113.                 LuaPath += loader.luaPath;
  2114.                 delete dir;
  2115.             }
  2116.         }
  2117.     }
  2118.     // Always grant access for the Lua hook
  2119.     luaHook->allowSystemAccess();
  2120.     luaHook->setLuaPath(LuaPath);
  2121.     int status = 0;
  2122.     // Execute the Lua hook initialization script
  2123.     if (config->luaHook != "")
  2124.     {
  2125.         string filename = config->luaHook;
  2126.         ifstream scriptfile(filename.c_str());
  2127.         if (!scriptfile.good())
  2128.         {
  2129.             char errMsg[1024];
  2130.             sprintf(errMsg, "Error opening LuaHook '%s'",  filename.c_str());
  2131.             if (alerter != NULL)
  2132.                 alerter->fatalError(errMsg);
  2133.             else
  2134.                 flash(errMsg);
  2135.         }
  2136.         if (progressNotifier)
  2137.             progressNotifier->update(config->luaHook);
  2138.         status = luaHook->loadScript(scriptfile, filename);
  2139.     }
  2140.     else
  2141.     {
  2142.         status = luaHook->loadScript("");
  2143.     }
  2144.     if (status != 0)
  2145.     {
  2146.         cout << "lua hook load failedn";
  2147.         string errMsg = luaHook->getErrorMessage();
  2148.         if (errMsg.empty())
  2149.             errMsg = "Unknown error loading hook script";
  2150.         if (alerter != NULL)
  2151.             alerter->fatalError(errMsg);
  2152.         else
  2153.             flash(errMsg);
  2154.         delete luaHook;
  2155.         luaHook = NULL;
  2156.     }
  2157.     else
  2158.     {
  2159.         // Coroutine execution; control may be transferred between the
  2160.         // script and Celestia's event loop
  2161.         if (!luaHook->createThread())
  2162.         {
  2163.             const char* errMsg = "Script coroutine initialization failed";
  2164.             cout << "hook thread failedn";
  2165.             if (alerter != NULL)
  2166.                 alerter->fatalError(errMsg);
  2167.             else
  2168.                 flash(errMsg);
  2169.             delete luaHook;
  2170.             luaHook = NULL;
  2171.         }
  2172.         if (luaHook)
  2173.         {
  2174.             while (!luaHook->tick(0.1)) ;
  2175.         }
  2176.     }
  2177.     // Set up the script context; if the system access policy is allow,
  2178.     // it will share the same context as the Lua hook. Otherwise, we
  2179.     // create a private context.
  2180.     if (config->scriptSystemAccessPolicy == "allow")
  2181.     {
  2182.         SetScriptedObjectContext(luaHook->getState());
  2183.     }
  2184.     else
  2185.     {
  2186.         luaSandbox = new LuaState();
  2187.         luaSandbox->init(this);
  2188.         // Allow access to functions in package because we need 'require'
  2189.         // But, loadlib is prohibited.
  2190.         luaSandbox->allowLuaPackageAccess();
  2191.         luaSandbox->setLuaPath(LuaPath);
  2192.         status = luaSandbox->loadScript("");
  2193.         if (status != 0)
  2194.         {
  2195.             delete luaSandbox;
  2196.             luaSandbox = NULL;
  2197.         }
  2198.         SetScriptedObjectContext(luaSandbox->getState());
  2199.     }
  2200.     return true;
  2201. }
  2202. #endif