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

OpenGL

开发平台:

Visual C++

  1. // body.cpp
  2. //
  3. // Copyright (C) 2001-2006 Chris Laurel <claurel@shatters.net>
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. #include <cstdlib>
  10. #include <cassert>
  11. #include <algorithm>
  12. #include <celmath/mathlib.h>
  13. #include <celutil/util.h>
  14. #include <celutil/utf8.h>
  15. #include "mesh.h"
  16. #include "meshmanager.h"
  17. #include "body.h"
  18. #include "frame.h"
  19. #include "timeline.h"
  20. #include "timelinephase.h"
  21. #include "frametree.h"
  22. #include "referencemark.h"
  23. using namespace std;
  24. Body::Body(PlanetarySystem* _system, const string& _name) :
  25.     names(1),
  26.     localizedNameIndex(0),
  27.     system(_system),
  28.     satellites(NULL),
  29.     timeline(NULL),
  30.     frameTree(NULL),
  31.     radius(1.0f),
  32.     semiAxes(1.0f, 1.0f, 1.0f),
  33.     mass(0.0f),
  34.     albedo(0.5f),
  35.     orientation(1.0f),
  36.     geometry(InvalidResource),
  37.     geometryScale(1.0f),
  38.     surface(Color(1.0f, 1.0f, 1.0f)),
  39.     atmosphere(NULL),
  40.     rings(NULL),
  41.     classification(Unknown),
  42.     altSurfaces(NULL),
  43.     locations(NULL),
  44.     locationsComputed(false),
  45.     referenceMarks(NULL),
  46.     visible(1),
  47.     clickable(1),
  48.     visibleAsPoint(1),
  49.     overrideOrbitColor(0),
  50.     orbitVisibility(UseClassVisibility),
  51.     secondaryIlluminator(true)
  52. {
  53.     setName(_name);
  54.     recomputeCullingRadius();
  55.     system->addBody(this);
  56. }
  57. Body::~Body()
  58. {
  59.     if (system != NULL)
  60.         system->removeBody(this);
  61.     // Remove from frame hierarchy
  62.     // Clean up the reference mark list
  63.     if (referenceMarks != NULL)
  64.     {
  65.         for (list<ReferenceMark*>::iterator iter = referenceMarks->begin(); iter != referenceMarks->end(); ++iter)
  66.             delete *iter;
  67.         delete referenceMarks;
  68.     }
  69.     delete timeline;
  70.     delete satellites;
  71.     delete frameTree;
  72. }
  73. /*! Reset body attributes to their default values. The object hierarchy is left untouched,
  74.  *  i.e. child objects are not removed. Alternate surfaces and locations are not removed
  75.  *  either.
  76.  */
  77. void Body::setDefaultProperties()
  78. {
  79.     radius = 1.0f;
  80.     semiAxes = Vec3f(1.0f, 1.0f, 1.0f);
  81.     mass = 0.0f;
  82.     albedo = 0.5f;
  83.     orientation = Quatf(1.0f);
  84.     geometry = InvalidResource;
  85.     surface = Surface(Color::White);
  86.     delete atmosphere;
  87.     atmosphere = NULL;
  88.     delete rings;
  89.     rings = NULL;
  90.     classification = Unknown;
  91.     visible = 1;
  92.     clickable = 1;
  93.     visibleAsPoint = 1;
  94.     overrideOrbitColor = 0;
  95.     orbitVisibility = UseClassVisibility;
  96.     recomputeCullingRadius();
  97. }
  98. /*! Return the list of all names (non-localized) by which this
  99.  *  body is known.
  100.  */
  101. const vector<string>& Body::getNames() const
  102. {
  103.     return names; 
  104. }
  105. /*! Return the primary name for the body; if i18n, return the
  106.  *  localized name of the body.
  107.  */
  108. string Body::getName(bool i18n) const
  109. {
  110.     if (!i18n) 
  111.         return names[0];
  112.     else
  113.         return names[localizedNameIndex];
  114. }
  115. /*! Get the localized name for the body. If no localized name
  116.  *  has been set, the primary name is returned.
  117.  */
  118. string Body::getLocalizedName() const
  119. {
  120.     return names[localizedNameIndex];
  121. }
  122. bool Body::hasLocalizedName() const
  123. {
  124.     return localizedNameIndex != 0;
  125. }
  126. /*! Set the primary name of the body. The localized name is updated
  127.  *  automatically as well.
  128.  *  Note: setName() is private, and only called from the Body constructor.
  129.  *  It shouldn't be called elsewhere.
  130.  */
  131. void Body::setName(const string& name)
  132. {
  133.     names[0] = name;
  134.     string localizedName = _(name.c_str());
  135.     if (name == localizedName)
  136.     {
  137.         // No localized name; set the localized name index to zero to
  138.         // indicate this.
  139.         localizedNameIndex = 0;
  140.     }
  141.     else
  142.     {
  143.         names.push_back(localizedName);
  144.         localizedNameIndex = names.size() - 1;
  145.     }
  146. }
  147. /*! Add a new name for this body. Aliases are non localized.
  148.  */
  149. void Body::addAlias(const string& alias)
  150. {
  151.     names.push_back(alias);
  152.     system->addAlias(this, alias);
  153. }
  154. PlanetarySystem* Body::getSystem() const
  155. {
  156.     return system;
  157. }
  158. FrameTree* Body::getFrameTree() const
  159. {
  160.     return frameTree;
  161. }
  162. FrameTree* Body::getOrCreateFrameTree()
  163. {
  164.     if (frameTree == NULL)
  165.         frameTree = new FrameTree(this);
  166.     return frameTree;
  167. }
  168. const Timeline* Body::getTimeline() const
  169. {
  170.     return timeline;
  171. }
  172. void Body::setTimeline(Timeline* newTimeline)
  173. {
  174.     if (timeline != newTimeline)
  175.     {
  176.         delete timeline;
  177.         timeline = newTimeline;
  178.         markChanged();
  179.     }
  180. }
  181. void Body::markChanged()
  182. {
  183.     if (timeline != NULL)
  184.         timeline->markChanged();
  185. }
  186. void Body::markUpdated()
  187. {
  188.     if (frameTree != NULL)
  189.         frameTree->markUpdated();
  190. }
  191. const ReferenceFrame* Body::getOrbitFrame(double tdb) const
  192. {
  193.     return timeline->findPhase(tdb)->orbitFrame();
  194. }
  195. const Orbit* Body::getOrbit(double tdb) const
  196. {
  197.     return timeline->findPhase(tdb)->orbit();
  198. }
  199. const ReferenceFrame* Body::getBodyFrame(double tdb) const
  200. {
  201.     return timeline->findPhase(tdb)->bodyFrame();
  202. }
  203. const RotationModel* Body::getRotationModel(double tdb) const
  204. {
  205.     return timeline->findPhase(tdb)->rotationModel();
  206. }
  207. /*! Get the radius of a sphere large enough to contain the primary
  208.  *  geometry of the object: either a mesh or an ellipsoid.
  209.  *  For an irregular (mesh) object, the radius is defined to be
  210.  *  the largest semi-axis of the axis-aligned bounding box.  The
  211.  *  radius of the smallest sphere containing the object is potentially
  212.  *  larger by a factor of sqrt(3).
  213.  *
  214.  *  This method does not consider additional object features
  215.  *  such as rings, atmospheres, or reference marks; use
  216.  *  getCullingRadius() for that.
  217.  */
  218. float Body::getBoundingRadius() const
  219. {
  220.     if (geometry == InvalidResource)
  221.         return radius;
  222.     else
  223.         return radius * 1.7320508f; // sqrt(3)
  224. }
  225. /*! Return the radius of sphere large enough to contain any geometry
  226.  *  associated with this object: the primary geometry, comet tail,
  227.  *  rings, atmosphere shell, cloud layers, or reference marks.
  228.  */
  229. float Body::getCullingRadius() const
  230. {
  231.     return cullingRadius;
  232. }
  233. float Body::getMass() const
  234. {
  235.     return mass;
  236. }
  237. void Body::setMass(float _mass)
  238. {
  239.     mass = _mass;
  240. }
  241. float Body::getAlbedo() const
  242. {
  243.     return albedo;
  244. }
  245. void Body::setAlbedo(float _albedo)
  246. {
  247.     albedo = _albedo;
  248. }
  249. Quatf Body::getOrientation() const
  250. {
  251.     return orientation;
  252. }
  253. void Body::setOrientation(const Quatf& q)
  254. {
  255.     orientation = q;
  256. }
  257. /*! Set the semiaxes of a body.
  258.  */
  259. void Body::setSemiAxes(const Vec3f& _semiAxes)
  260. {
  261.     semiAxes = _semiAxes;
  262.     // Radius will always be the largest of the three semi axes
  263.     radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
  264.     recomputeCullingRadius();
  265. }
  266. /*! Retrieve the body's semiaxes
  267.  */
  268. Vec3f Body::getSemiAxes() const
  269. {
  270.     return semiAxes;
  271. }
  272. /*! Get the radius of the body. For a spherical body, this is simply
  273.  *  the sphere's radius. For an ellipsoidal body, the radius is the
  274.  *  largest of the three semiaxes. For irregular bodies (with a shape
  275.  *  represented by a mesh), the radius is the largest semiaxis of the
  276.  *  mesh's axis aligned bounding axis. Note that this means some portions
  277.  *  of the mesh may extend outside the sphere of the retrieved radius.
  278.  *  To obtain the radius of a sphere that will definitely enclose the
  279.  *  body, call getBoundingRadius() instead.
  280.  */
  281. float Body::getRadius() const
  282. {
  283.     return radius;
  284. }
  285. /*! Return true if the body is a perfect sphere.
  286. */
  287. bool Body::isSphere() const
  288. {
  289.     return (geometry == InvalidResource) &&
  290.            (semiAxes.x == semiAxes.y) && 
  291.            (semiAxes.x == semiAxes.z);
  292. }
  293. /*! Return true if the body is ellipsoidal, with geometry determined
  294.  *  completely by its semiaxes rather than a triangle based model.
  295.  */
  296. bool Body::isEllipsoid() const
  297. {
  298.     return geometry == InvalidResource;
  299. }
  300. const Surface& Body::getSurface() const
  301. {
  302.     return surface;
  303. }
  304. Surface& Body::getSurface()
  305. {
  306.     return surface;
  307. }
  308. void Body::setSurface(const Surface& surf)
  309. {
  310.     surface = surf;
  311. }
  312. void Body::setGeometry(ResourceHandle _geometry)
  313. {
  314.     geometry = _geometry;
  315. }
  316. /*! Set the scale factor for geometry; this is only used with unnormalized meshes.
  317.  *  When a mesh is normalized, the effective scale factor is the radius.
  318.  */
  319. void Body::setGeometryScale(float scale)
  320. {
  321.     geometryScale = scale;
  322. }
  323. PlanetarySystem* Body::getSatellites() const
  324. {
  325.     return satellites;
  326. }
  327. void Body::setSatellites(PlanetarySystem* ssys)
  328. {
  329.     satellites = ssys;
  330. }
  331. RingSystem* Body::getRings() const
  332. {
  333.     return rings;
  334. }
  335. void Body::setRings(const RingSystem& _rings)
  336. {
  337.     if (rings == NULL)
  338.         rings = new RingSystem(_rings);
  339.     else
  340.         *rings = _rings;
  341.     recomputeCullingRadius();
  342. }
  343. const Atmosphere* Body::getAtmosphere() const
  344. {
  345.     return atmosphere;
  346. }
  347. Atmosphere* Body::getAtmosphere()
  348. {
  349.     return atmosphere;
  350. }
  351. void Body::setAtmosphere(const Atmosphere& _atmosphere)
  352. {
  353.     if (atmosphere == NULL)
  354.         atmosphere = new Atmosphere();
  355.     *atmosphere = _atmosphere;
  356.     recomputeCullingRadius();
  357. }
  358. // The following four functions are used to get the state of the body
  359. // in universal coordinates:
  360. //    * getPosition
  361. //    * getOrientation
  362. //    * getVelocity
  363. //    * getAngularVelocity
  364. /*! Get the position of the body in the universal coordinate system.
  365.  *  This method uses high-precision coordinates and is thus
  366.  *  slower relative to getAstrocentricPosition(), which works strictly
  367.  *  with standard double precision. For most purposes,
  368.  *  getAstrocentricPosition() should be used instead of the more
  369.  *  general getPosition().
  370.  */
  371. UniversalCoord Body::getPosition(double tdb) const
  372. {
  373.     Point3d position(0.0, 0.0, 0.0);
  374.     const TimelinePhase* phase = timeline->findPhase(tdb);
  375.     Point3d p = phase->orbit()->positionAtTime(tdb);
  376.     ReferenceFrame* frame = phase->orbitFrame();
  377.     while (frame->getCenter().getType() == Selection::Type_Body)
  378.     {
  379.         phase = frame->getCenter().body()->timeline->findPhase(tdb);
  380.         position += Vec3d(p.x, p.y, p.z) * frame->getOrientation(tdb).toMatrix3();
  381.         p = phase->orbit()->positionAtTime(tdb);
  382.         frame = phase->orbitFrame();
  383.     }
  384.     position += Vec3d(p.x, p.y, p.z) * frame->getOrientation(tdb).toMatrix3();
  385.     if (frame->getCenter().star())
  386.         return astro::universalPosition(position, frame->getCenter().star()->getPosition(tdb));
  387.     else
  388.         return astro::universalPosition(position, frame->getCenter().getPosition(tdb));
  389. }
  390. /*! Get the orientation of the body in the universal coordinate system.
  391.  */
  392. Quatd Body::getOrientation(double tdb) const
  393. {
  394.     // TODO: This method overloads getOrientation(), but the two do very
  395.     // different things. The other method should be renamed to
  396.     // getModelOrientation().
  397.     const TimelinePhase* phase = timeline->findPhase(tdb);
  398.     return phase->rotationModel()->orientationAtTime(tdb) *
  399.            phase->bodyFrame()->getOrientation(tdb);
  400. }
  401. /*! Get the velocity of the body in the universal frame.
  402.  */
  403. Vec3d Body::getVelocity(double tdb) const
  404. {
  405.     const TimelinePhase* phase = timeline->findPhase(tdb);
  406.     ReferenceFrame* orbitFrame = phase->orbitFrame();
  407.     Vec3d v = phase->orbit()->velocityAtTime(tdb);
  408.     v = v * orbitFrame->getOrientation(tdb).toMatrix3() + orbitFrame->getCenter().getVelocity(tdb);
  409. if (!orbitFrame->isInertial())
  410. {
  411. Vec3d r = Selection(const_cast<Body*>(this)).getPosition(tdb) - orbitFrame->getCenter().getPosition(tdb);
  412. r = r * astro::microLightYearsToKilometers(1.0);
  413. v += cross(orbitFrame->getAngularVelocity(tdb), r);
  414. }
  415. return v;
  416. }
  417. /*! Get the angular velocity of the body in the universal frame.
  418.  */
  419. Vec3d Body::getAngularVelocity(double tdb) const
  420. {
  421.     const TimelinePhase* phase = timeline->findPhase(tdb);
  422.     Vec3d v = phase->rotationModel()->angularVelocityAtTime(tdb);
  423.     ReferenceFrame* bodyFrame = phase->bodyFrame();
  424. v = v * bodyFrame->getOrientation(tdb).toMatrix3();
  425. if (!bodyFrame->isInertial())
  426. {
  427. v += bodyFrame->getAngularVelocity(tdb);
  428. }
  429.     return v;
  430. }
  431. /*! Get the transformation which converts body coordinates into
  432.  *  astrocentric coordinates. Some clarification on the meaning
  433.  *  of 'astrocentric': the position of every solar system body
  434.  *  is ultimately defined with respect to some star or star
  435.  *  system barycenter.
  436.  */
  437. Mat4d Body::getLocalToAstrocentric(double tdb) const
  438. {
  439.     const TimelinePhase* phase = timeline->findPhase(tdb);
  440.     Point3d p = phase->orbitFrame()->convertToAstrocentric(phase->orbit()->positionAtTime(tdb), tdb);
  441.     return Mat4d::translation(p);
  442. }
  443. /*! Get the position of the center of the body in astrocentric ecliptic coordinates.
  444.  */
  445. Point3d Body::getAstrocentricPosition(double tdb) const
  446. {
  447.     // TODO: Switch the iterative method used in getPosition
  448.     const TimelinePhase* phase = timeline->findPhase(tdb);
  449.     return phase->orbitFrame()->convertToAstrocentric(phase->orbit()->positionAtTime(tdb), tdb);
  450. }
  451. /*! Get a rotation that converts from the ecliptic frame to the body frame.
  452.  */
  453. Quatd Body::getEclipticToFrame(double tdb) const
  454. {
  455.     const TimelinePhase* phase = timeline->findPhase(tdb);
  456.     return phase->bodyFrame()->getOrientation(tdb);
  457. }
  458. /*! Get a rotation that converts from the ecliptic frame to the body's 
  459.  *  mean equatorial frame.
  460.  */
  461. Quatd Body::getEclipticToEquatorial(double tdb) const
  462. {
  463.     const TimelinePhase* phase = timeline->findPhase(tdb);
  464.     return phase->rotationModel()->equatorOrientationAtTime(tdb) *
  465.            phase->bodyFrame()->getOrientation(tdb); 
  466. }
  467. /*! Get a rotation that converts from the ecliptic frame to this
  468.  *  objects's body fixed frame.
  469.  */
  470. Quatd Body::getEclipticToBodyFixed(double tdb) const
  471. {
  472.     const TimelinePhase* phase = timeline->findPhase(tdb);
  473.     return phase->rotationModel()->orientationAtTime(tdb) *
  474.            phase->bodyFrame()->getOrientation(tdb);
  475. }
  476. // The body-fixed coordinate system has an origin at the center of the
  477. // body, y-axis parallel to the rotation axis, x-axis through the prime
  478. // meridian, and z-axis at a right angle the xy plane.
  479. Quatd Body::getEquatorialToBodyFixed(double tdb) const
  480. {
  481.     const TimelinePhase* phase = timeline->findPhase(tdb);
  482.     return phase->rotationModel()->spin(tdb);
  483. }
  484. /*! Get a transformation to convert from the object's body fixed frame
  485.  *  to the astrocentric ecliptic frame.
  486.  */
  487. Mat4d Body::getBodyFixedToAstrocentric(double tdb) const
  488. {
  489.     return getEquatorialToBodyFixed(tdb).toMatrix4() * getLocalToAstrocentric(tdb);
  490. }
  491. Vec3d Body::planetocentricToCartesian(double lon, double lat, double alt) const
  492. {
  493.     double phi = -degToRad(lat) + PI / 2;
  494.     double theta = degToRad(lon) - PI;
  495.     Vec3d pos(cos(theta) * sin(phi),
  496.               cos(phi),
  497.               -sin(theta) * sin(phi));
  498.     return pos * (getRadius() + alt);
  499. }
  500. Vec3d Body::planetocentricToCartesian(const Vec3d& lonLatAlt) const
  501. {
  502.     return planetocentricToCartesian(lonLatAlt.x, lonLatAlt.y, lonLatAlt.z);
  503. }
  504. /*! Convert cartesian body-fixed coordinates to spherical planetocentric
  505.  *  coordinates.
  506.  */
  507. Vec3d Body::cartesianToPlanetocentric(const Vec3d& v) const
  508. {
  509.     Vec3d w = v;
  510.     w.normalize();
  511.     double lat = PI / 2.0 - acos(w.y);
  512.     double lon = atan2(w.z, -w.x);
  513.     return Vec3d(lon, lat, v.length() - getRadius());
  514. }
  515. /*! Convert body-centered ecliptic coordinates to spherical planetocentric
  516.  *  coordinates.
  517.  */
  518. Vec3d Body::eclipticToPlanetocentric(const Vec3d& ecl, double tdb) const
  519. {
  520.     Quatd q = getEclipticToBodyFixed(tdb);
  521.     Vec3d bf = ecl * (~q).toMatrix3();
  522.     return cartesianToPlanetocentric(bf);
  523. }
  524. bool Body::extant(double t) const
  525. {
  526.     return timeline->includes(t);
  527. }
  528. void Body::getLifespan(double& begin, double& end) const
  529. {
  530.     begin = timeline->startTime();
  531.     end = timeline->endTime();
  532. }
  533. float Body::getLuminosity(const Star& sun,
  534.                           float distanceFromSun) const
  535. {
  536.     return getLuminosity(sun.getLuminosity(), distanceFromSun);
  537. }
  538. float Body::getLuminosity(float sunLuminosity,
  539.                           float distanceFromSun) const
  540. {
  541.     // Compute the total power of the star in Watts
  542.     double power = astro::SOLAR_POWER * sunLuminosity;
  543.     // Compute the irradiance at a distance of 1au from the star in W/m^2
  544.     // double irradiance = power / sphereArea(astro::AUtoKilometers(1.0) * 1000);
  545.     // Compute the irradiance at the body's distance from the star
  546.     double satIrradiance = power / sphereArea(distanceFromSun * 1000);
  547.     // Compute the total energy hitting the planet
  548.     double incidentEnergy = satIrradiance * circleArea(radius * 1000);
  549.     double reflectedEnergy = incidentEnergy * albedo;
  550.     
  551.     // Compute the luminosity (i.e. power relative to solar power)
  552.     return (float) (reflectedEnergy / astro::SOLAR_POWER);
  553. }
  554. /*! Get the apparent magnitude of the body, neglecting the phase (as if
  555.  *  the body was at opposition.
  556.  */
  557. float Body::getApparentMagnitude(const Star& sun,
  558.                                  float distanceFromSun,
  559.                                  float distanceFromViewer) const
  560. {
  561.     return astro::lumToAppMag(getLuminosity(sun, distanceFromSun),
  562.                               astro::kilometersToLightYears(distanceFromViewer));
  563. }
  564. /*! Get the apparent magnitude of the body, neglecting the phase (as if
  565.  *  the body was at opposition.
  566.  */
  567. float Body::getApparentMagnitude(float sunLuminosity,
  568.                                  float distanceFromSun,
  569.                                  float distanceFromViewer) const
  570. {
  571.     return astro::lumToAppMag(getLuminosity(sunLuminosity, distanceFromSun),
  572.                               astro::kilometersToLightYears(distanceFromViewer));
  573. }
  574. /*! Get the apparent magnitude of the body, corrected for its phase.
  575.  */
  576. float Body::getApparentMagnitude(const Star& sun,
  577.                                  const Vec3d& sunPosition,
  578.                                  const Vec3d& viewerPosition) const
  579. {
  580.     return getApparentMagnitude(sun.getLuminosity(),
  581.                                 sunPosition,
  582.                                 viewerPosition);
  583. }
  584. /*! Get the apparent magnitude of the body, corrected for its phase.
  585.  */
  586. float Body::getApparentMagnitude(float sunLuminosity,
  587.                                  const Vec3d& sunPosition,
  588.                                  const Vec3d& viewerPosition) const
  589. {
  590.     double distanceToViewer = viewerPosition.length();
  591.     double distanceToSun = sunPosition.length();
  592.     float illuminatedFraction = (float) (1.0 + (viewerPosition / distanceToViewer) *
  593.                                          (sunPosition / distanceToSun)) / 2.0f;
  594.     return astro::lumToAppMag(getLuminosity(sunLuminosity, (float) distanceToSun) * illuminatedFraction, (float) astro::kilometersToLightYears(distanceToViewer));
  595. }
  596. int Body::getClassification() const
  597. {
  598.     return classification;
  599. }
  600. void Body::setClassification(int _classification)
  601. {
  602.     classification = _classification;
  603.     recomputeCullingRadius();
  604.     markChanged();
  605. }
  606. /*! Return the effective classification of this body used when rendering
  607.  *  orbits. Normally, this is just the classification of the object, but
  608.  *  invisible objects are treated specially: they behave as if they have
  609.  *  the classification of their child objects. This fixes annoyances when
  610.  *  planets are defined with orbits relative to their system barycenters.
  611.  *  For example, Pluto's orbit can seen in a solar system scale view, even
  612.  *  though its orbit is defined relative to the Pluto-Charon barycenter
  613.  *  and is this just a few hundred kilometers in size.
  614.  */
  615. int Body::getOrbitClassification() const
  616. {
  617.     if (classification != Invisible || frameTree == NULL)
  618.     {
  619.         return classification;
  620.     }
  621.     else
  622.     {
  623.         int orbitClass = frameTree->childClassMask();
  624.         if (orbitClass & Planet)
  625.             return Planet;
  626.         else if (orbitClass & DwarfPlanet)
  627.             return DwarfPlanet;
  628.         else if (orbitClass & Moon)
  629.             return Moon;
  630.         else if (orbitClass & MinorMoon)
  631.             return MinorMoon;
  632.         else if (orbitClass & Asteroid)
  633.             return Asteroid;
  634.         else if (orbitClass & Spacecraft)
  635.             return Spacecraft;
  636.         else
  637.             return Invisible;
  638.     }
  639. }
  640. string Body::getInfoURL() const
  641. {
  642.     return infoURL;
  643. }
  644. void Body::setInfoURL(const string& _infoURL)
  645. {
  646.     infoURL = _infoURL;
  647. }
  648. Surface* Body::getAlternateSurface(const string& name) const
  649. {
  650.     if (altSurfaces == NULL)
  651.         return NULL;
  652.     AltSurfaceTable::iterator iter = altSurfaces->find(name);
  653.     if (iter == altSurfaces->end())
  654.         return NULL;
  655.     else
  656.         return iter->second;
  657. }
  658. void Body::addAlternateSurface(const string& name, Surface* surface)
  659. {
  660.     if (altSurfaces == NULL)
  661.         altSurfaces = new AltSurfaceTable();
  662.     //altSurfaces->insert(AltSurfaceTable::value_type(name, surface));
  663.     (*altSurfaces)[name] = surface;
  664. }
  665. vector<string>* Body::getAlternateSurfaceNames() const
  666. {
  667.     vector<string>* names = new vector<string>();
  668.     if (altSurfaces != NULL)
  669.     {
  670.         for (AltSurfaceTable::const_iterator iter = altSurfaces->begin();
  671.              iter != altSurfaces->end(); iter++)
  672.         {
  673.             names->insert(names->end(), iter->first);
  674.         }
  675.     }
  676.     return names;
  677. }
  678. void Body::addLocation(Location* loc)
  679. {
  680.     assert(loc != NULL);
  681.     if (loc == NULL)
  682.         return;
  683.     if (locations == NULL)
  684.         locations = new vector<Location*>();
  685.     locations->insert(locations->end(), loc);
  686.     loc->setParentBody(this);
  687. }
  688. vector<Location*>* Body::getLocations() const
  689. {
  690.     return locations;
  691. }
  692. Location* Body::findLocation(const string& name, bool i18n) const
  693. {
  694.     if (locations == NULL)
  695.         return NULL;
  696.     for (vector<Location*>::const_iterator iter = locations->begin();
  697.          iter != locations->end(); iter++)
  698.     {
  699.         if (!UTF8StringCompare(name, (*iter)->getName(i18n)))
  700.             return *iter;
  701.     }
  702.     return NULL;
  703. }
  704. // Compute the positions of locations on an irregular object using ray-mesh
  705. // intersections.  This is not automatically done when a location is added
  706. // because it would force the loading of all meshes for objects with 
  707. // defined locations; on-demand (i.e. when the object becomes visible to
  708. // a user) loading of meshes is preferred.
  709. void Body::computeLocations()
  710. {
  711.     if (locationsComputed)
  712.         return;
  713.     locationsComputed = true;
  714.     // No work to do if there's no mesh, or if the mesh cannot be loaded
  715.     if (geometry == InvalidResource)
  716.         return;
  717.     Geometry* g = GetGeometryManager()->find(geometry);
  718.     if (g == NULL)
  719.         return;
  720.     // TODO: Implement separate radius and bounding radius so that this hack is
  721.     // not necessary.
  722.     double boundingRadius = 2.0;
  723.     for (vector<Location*>::const_iterator iter = locations->begin();
  724.          iter != locations->end(); iter++)
  725.     {
  726.         Vec3f v = (*iter)->getPosition();
  727.         float alt = v.length() - radius;
  728.         if (alt != -radius)
  729.             v.normalize();
  730.         v *= (float) boundingRadius;
  731.         Ray3d ray(Point3d(v.x, v.y, v.z), Vec3d(-v.x, -v.y, -v.z));
  732.         double t = 0.0;
  733.         if (g->pick(ray, t))
  734.         {
  735.             v *= (float) ((1.0 - t) * radius + alt);
  736.             (*iter)->setPosition(v);
  737.         }
  738.     }
  739. }
  740. /*! Add a new reference mark.
  741.  */
  742. void
  743. Body::addReferenceMark(ReferenceMark* refMark)
  744. {
  745.     if (referenceMarks == NULL)
  746.         referenceMarks = new list<ReferenceMark*>();
  747.     referenceMarks->push_back(refMark);
  748.     recomputeCullingRadius();
  749. }
  750. /*! Remove the first reference mark with the specified tag.
  751.  */
  752. void
  753. Body::removeReferenceMark(const string& tag)
  754. {
  755.     if (referenceMarks != NULL)
  756.     {
  757.         ReferenceMark* refMark = findReferenceMark(tag);
  758.         if (refMark != NULL)
  759.         {
  760.             referenceMarks->remove(refMark);
  761.             delete refMark;
  762.             recomputeCullingRadius();
  763.         }
  764.     }
  765. }
  766. /*! Find the first reference mark with the specified tag. If the body has
  767.  *  no reference marks with the specified tag, this method will return
  768.  *  NULL.
  769.  */
  770. ReferenceMark*
  771. Body::findReferenceMark(const string& tag) const
  772. {
  773.     if (referenceMarks != NULL)
  774.     {
  775.         for (list<ReferenceMark*>::iterator iter = referenceMarks->begin(); iter != referenceMarks->end(); ++iter)
  776.         {
  777.             if ((*iter)->getTag() == tag)
  778.                 return *iter;
  779.         }
  780.     }
  781.     return NULL;
  782. }
  783. /*! Get the list of reference marks associated with this body. May return
  784.  *  NULL if there are no reference marks.
  785.  */
  786. const list<ReferenceMark*>*
  787. Body::getReferenceMarks() const
  788. {
  789.     return referenceMarks;
  790. }
  791. /*! Sets whether or not the object is visible.
  792.  */
  793. void Body::setVisible(bool _visible)
  794. {
  795.     visible = _visible ? 1 : 0;
  796. }
  797. /*! Sets whether or not the object can be selected by clicking on
  798.  *  it. If set to false, the object is completely ignored when the
  799.  *  user clicks it, making it possible to select background objects.
  800.  */
  801. void Body::setClickable(bool _clickable)
  802. {
  803.     clickable = _clickable ? 1 : 0;
  804. }
  805. /*! Set whether or not the object is visible as a starlike point
  806.  *  when it occupies less than a pixel onscreen. This is appropriate
  807.  *  for planets and moons, but generally not desireable for buildings
  808.  *  or spacecraft components.
  809.  */
  810. void Body::setVisibleAsPoint(bool _visibleAsPoint)
  811. {
  812.     visibleAsPoint = _visibleAsPoint ? 1 : 0;
  813. }
  814. /*! The orbitColorOverride flag is set to true if an alternate orbit
  815.  *  color should be used (specified via setOrbitColor) instead of the
  816.  *  default class orbit color.
  817.  */
  818. void Body::setOrbitColorOverridden(bool override)
  819. {
  820.     overrideOrbitColor = override ? 1 : 0;
  821. }
  822. /*! Set the visibility policy for the orbit of this object:
  823.  *  - NeverVisible: Never show the orbit of this object.
  824.  *  - UseClassVisibility: (Default) Show the orbit of this object
  825.  *    its class is enabled in the orbit mask.
  826.  *  - AlwaysVisible: Always show the orbit of this object whenever
  827.  *    orbit paths are enabled.
  828.  */
  829. void Body::setOrbitVisibility(VisibilityPolicy _orbitVisibility)
  830. {
  831.     orbitVisibility = _orbitVisibility;
  832. }
  833. /*! Set the color used when rendering the orbit. This is only used
  834.  *  when the orbitColorOverride flag is set to true; otherwise, the
  835.  *  standard orbit color for all objects of the class is used.
  836.  */
  837. void Body::setOrbitColor(const Color& c)
  838. {
  839.     orbitColor = c;
  840. }
  841. /*! Set whether or not the object should be considered when calculating
  842.  *  secondary illumination (e.g. planetshine.)
  843.  */
  844. void Body::setSecondaryIlluminator(bool enable)
  845. {
  846.     if (enable != secondaryIlluminator)
  847.     {
  848.         markChanged();
  849.         secondaryIlluminator = enable;
  850.     }
  851. }
  852. void Body::recomputeCullingRadius()
  853. {
  854.     float r = getBoundingRadius();
  855.     if (rings != NULL)
  856.         r = max(r, rings->outerRadius);
  857.     if (atmosphere != NULL)
  858.     {
  859.         r = max(r, atmosphere->height);
  860.         r = max(r, atmosphere->cloudHeight);
  861.     }
  862.     if (referenceMarks != NULL)
  863.     {
  864.         for (std::list<ReferenceMark*>::const_iterator iter = referenceMarks->begin();
  865.              iter != referenceMarks->end(); iter++)
  866.         {
  867.             r = max(r, (*iter)->boundingSphereRadius());
  868.         }
  869.     }
  870.     if (classification == Body::Comet)
  871.         r = max(r, astro::AUtoKilometers(1.0f));
  872.     if (r != cullingRadius)
  873.     {
  874.         cullingRadius = r;
  875.         markChanged();
  876.     }
  877. }
  878. /**** Implementation of PlanetarySystem ****/
  879. /*! Return the equatorial frame for this object. This frame is used as
  880.  *  the default frame for objects in SSC files that orbit non-stellar bodies.
  881.  *  In order to avoid wasting memory, it is created until the first time it
  882.  *  is requested.
  883.  */
  884. PlanetarySystem::PlanetarySystem(Body* _primary) :
  885.     star(NULL),
  886.     primary(_primary)
  887. {
  888.     if (primary != NULL && primary->getSystem() != NULL)
  889.         star = primary->getSystem()->getStar();
  890. }
  891. PlanetarySystem::PlanetarySystem(Star* _star) :
  892.     star(_star),
  893.     primary(NULL)
  894. {
  895. }
  896. PlanetarySystem::~PlanetarySystem()
  897. {
  898. }
  899. /*! Add a new alias for an object. If an object with the specified
  900.  *  alias already exists in the planetary system, the old entry will
  901.  *  be replaced.
  902.  */
  903. void PlanetarySystem::addAlias(Body* body, const string& alias)
  904. {
  905.     assert(body->getSystem() == this);
  906.     
  907.     objectIndex.insert(make_pair(alias, body));
  908. }
  909. /*! Remove the an alias for an object. This method does nothing
  910.  *  if the alias is not present in the index, or if the alias
  911.  *  refers to a different object.
  912.  */
  913. void PlanetarySystem::removeAlias(const Body* body, const string& alias)
  914. {
  915.     assert(body->getSystem() == this);
  916.     
  917.     ObjectIndex::iterator iter = objectIndex.find(alias);
  918.     if (iter != objectIndex.end())
  919.     {
  920.         if (iter->second == body)
  921.             objectIndex.erase(iter);
  922.     }
  923. }
  924. void PlanetarySystem::addBody(Body* body)
  925. {
  926.     satellites.insert(satellites.end(), body);
  927.     addBodyToNameIndex(body);
  928. }
  929. // Add all aliases for the body to the name index
  930. void PlanetarySystem::addBodyToNameIndex(Body* body)
  931. {
  932.     const vector<string>& names = body->getNames();
  933.     for (vector<string>::const_iterator iter = names.begin(); iter != names.end(); iter++)
  934.     {
  935.         objectIndex.insert(make_pair(*iter, body));
  936.     }
  937. }
  938. // Remove all references to the body in the name index.
  939. void PlanetarySystem::removeBodyFromNameIndex(const Body* body)
  940. {
  941.     assert(body->getSystem() == this);
  942.     
  943.     // Erase the object from the object indices
  944.     const vector<string>& names = body->getNames();
  945.     for (vector<string>::const_iterator iter = names.begin(); iter != names.end(); iter++)
  946.     {
  947.         removeAlias(body, *iter);
  948.     } 
  949. }
  950. void PlanetarySystem::removeBody(Body* body)
  951. {
  952.     for (vector<Body*>::iterator iter = satellites.begin();
  953.          iter != satellites.end(); iter++)
  954.     {
  955.         if (*iter == body)
  956.         {
  957.             satellites.erase(iter);
  958.             break;
  959.         }
  960.     }
  961.     
  962.     removeBodyFromNameIndex(body);
  963. }
  964. void PlanetarySystem::replaceBody(Body* oldBody, Body* newBody)
  965. {
  966.     for (vector<Body*>::iterator iter = satellites.begin();
  967.          iter != satellites.end(); iter++)
  968.     {
  969.         if (*iter == oldBody)
  970.         {
  971.             *iter = newBody;
  972.             break;
  973.         }
  974.     }
  975.     removeBodyFromNameIndex(oldBody);
  976.     addBodyToNameIndex(newBody);
  977. }
  978. /*! Find a body with the specified name within a planetary system.
  979.  * 
  980.  *  deepSearch: if true, recursively search the systems of child objects
  981.  *  i18n: if true, allow matching of localized body names. When responding
  982.  *    to a user query, this flag should be true. In other cases--such
  983.  *    as resolving an object name in an ssc file--it should be false. Otherwise,
  984.  *    object lookup will behave differently based on the locale.
  985.  */
  986. Body* PlanetarySystem::find(const string& _name, bool deepSearch, bool i18n) const
  987. {
  988.     ObjectIndex::const_iterator firstMatch = objectIndex.find(_name);
  989.     if (firstMatch != objectIndex.end())
  990.     {
  991.         Body* matchedBody = firstMatch->second;
  992.         
  993.         if (i18n)
  994.         {
  995.             return matchedBody;
  996.         }
  997.         else
  998.         {
  999.             // Ignore localized names
  1000.             if (!matchedBody->hasLocalizedName() || _name != matchedBody->getLocalizedName())
  1001.                 return matchedBody;
  1002.         }
  1003.     }
  1004.     if (deepSearch)
  1005.     {
  1006.         for (vector<Body*>::const_iterator iter = satellites.begin();
  1007.              iter != satellites.end(); iter++)
  1008.         {
  1009.             if (UTF8StringCompare((*iter)->getName(i18n), _name) == 0)
  1010.             {
  1011.                 return *iter;
  1012.             }
  1013.             else if (deepSearch && (*iter)->getSatellites() != NULL)
  1014.             {
  1015.                 Body* body = (*iter)->getSatellites()->find(_name, deepSearch, i18n);
  1016.                 if (body != NULL)
  1017.                     return body;
  1018.             }
  1019.         }
  1020.     }
  1021.     return NULL;
  1022. }
  1023. bool PlanetarySystem::traverse(TraversalFunc func, void* info) const
  1024. {
  1025.     for (int i = 0; i < getSystemSize(); i++)
  1026.     {
  1027.         Body* body = getBody(i);
  1028.         // assert(body != NULL);
  1029.         if (!func(body, info))
  1030.             return false;
  1031.         if (body->getSatellites() != NULL)
  1032.         {
  1033.             if (!body->getSatellites()->traverse(func, info))
  1034.                 return false;
  1035.         }
  1036.     }
  1037.     return true;
  1038. }
  1039. std::vector<std::string> PlanetarySystem::getCompletion(const std::string& _name, bool deepSearch) const
  1040. {
  1041.     std::vector<std::string> completion;
  1042.     int _name_length = UTF8Length(_name);
  1043.     // Search through all names in this planetary system.
  1044.     for (ObjectIndex::const_iterator iter = objectIndex.begin();
  1045.          iter != objectIndex.end(); iter++)
  1046.     {
  1047.         const string& alias = iter->first;
  1048.         
  1049.         if (UTF8StringCompare(alias, _name, _name_length) == 0)
  1050.         {
  1051.             completion.push_back(alias);
  1052.         }
  1053.     }
  1054.     
  1055.     // Scan child objects
  1056.     if (deepSearch)
  1057.     {
  1058.         for (vector<Body*>::const_iterator iter = satellites.begin();
  1059.              iter != satellites.end(); iter++)
  1060.         {
  1061.             if ((*iter)->getSatellites() != NULL)
  1062.             {
  1063.                 vector<string> bodies = (*iter)->getSatellites()->getCompletion(_name);
  1064.                 completion.insert(completion.end(), bodies.begin(), bodies.end());
  1065.             }
  1066.         }
  1067.     }
  1068.     
  1069.     return completion;
  1070. }
  1071. /*! Get the order of the object in the list of children. Returns -1 if the
  1072.  *  specified body is not a child object.
  1073.  */
  1074. int PlanetarySystem::getOrder(const Body* body) const
  1075. {
  1076.     vector<Body*>::const_iterator iter = std::find(satellites.begin(), satellites.end(), body);
  1077.     if (iter == satellites.end())
  1078.         return -1;
  1079.     else
  1080.         return iter - satellites.begin();
  1081. }