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

OpenGL

开发平台:

Visual C++

  1. // solarsys.cpp
  2. //
  3. // Copyright (C) 2001-2006 Chris Laurel <claurel@shatters.net>
  4. //
  5. // Solar system catalog parser.
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. #include <cassert>
  12. // #include <limits>
  13. #include <cstdio>
  14. #ifndef _WIN32
  15. #ifndef TARGET_OS_MAC
  16. #include <config.h>
  17. #endif /* ! TARGET_OS_MAC */
  18. #endif /* ! _WIN32 */
  19. #include <celutil/debug.h>
  20. #include <celmath/mathlib.h>
  21. #include <celutil/util.h>
  22. #include <cstdio>
  23. #include <limits>
  24. #include "astro.h"
  25. #include "parser.h"
  26. #include "texmanager.h"
  27. #include "meshmanager.h"
  28. #include "universe.h"
  29. #include "multitexture.h"
  30. #include "parseobject.h"
  31. #include "frametree.h"
  32. #include "timeline.h"
  33. #include "timelinephase.h"
  34. using namespace std;
  35. enum Disposition
  36. {
  37.     AddObject,
  38.     ReplaceObject,
  39.     ModifyObject,
  40. };
  41. enum BodyType
  42. {
  43.     ReferencePoint,
  44.     NormalBody,
  45.     SurfaceObject,
  46.     UnknownBodyType,
  47. };
  48. /*!
  49.   Solar system catalog (.ssc) files contain items of three different types:
  50.   bodies, locations, and alternate surfaces.  Bodies planets, moons, asteroids,
  51.   comets, and spacecraft.  Locations are points on the surfaces of bodies which
  52.   may be labelled but aren't rendered.  Alternate surfaces are additional
  53.   surface definitions for bodies.
  54.   An ssc file contains zero or more definitions of this form:
  55.   code
  56.   [disposition] [item type] "name" "parent name"
  57.   {
  58.      ...object info fields...
  59.   }
  60.   endcode
  61.   The disposition of the object determines what happens if an item with the
  62.   same parent and same name already exists.  It may be one of the following:
  63.   - Add - Default if none is specified.  Add the item even if one of the
  64.     same name already exists.
  65.   - Replace - Replace an existing item with the new one
  66.   - Modify - Modify the existing item, changing the fields that appear
  67.     in the new definition.
  68.   All dispositions are equivalent to add if no item of the same name
  69.   already exists.
  70.   The item type is one of Body, Location, or AltSurface, defaulting to
  71.   Body when no type is given.
  72.   The name and parent name are both mandatory.
  73. */
  74. static void errorMessagePrelude(const Tokenizer& tok)
  75. {
  76.     cerr << _("Error in .ssc file (line ") << tok.getLineNumber() << "): ";
  77. }
  78. static void sscError(const Tokenizer& tok,
  79.                      const string& msg)
  80. {
  81.     errorMessagePrelude(tok);
  82.     cerr << msg << 'n';
  83. }
  84. // Object class properties
  85. static const int CLASSES_UNCLICKABLE           = Body::Invisible |
  86.                                                  Body::Diffuse;
  87. static const int CLASSES_INVISIBLE_AS_POINT    = Body::Invisible      |
  88.                                                  Body::SurfaceFeature |
  89.                                                  Body::Component      |
  90.                                                  Body::Diffuse;
  91. static const int CLASSES_SECONDARY_ILLUMINATOR = Body::Planet      |
  92.                                                  Body::Moon        |
  93.                                                  Body::MinorMoon   |
  94.                                                  Body::DwarfPlanet |
  95.                                                  Body::Asteroid    |
  96.                                                  Body::Comet;
  97. typedef map<std::string, int, CompareIgnoringCasePredicate> ClassificationTable;
  98. static ClassificationTable Classifications;
  99. void InitializeClassifications()
  100. {
  101.     Classifications["planet"]         = Body::Planet;
  102.     Classifications["dwarfplanet"]    = Body::DwarfPlanet;
  103.     Classifications["moon"]           = Body::Moon;
  104.     Classifications["minormoon"]      = Body::MinorMoon;
  105.     Classifications["comet"]          = Body::Comet;
  106.     Classifications["asteroid"]       = Body::Asteroid;
  107.     Classifications["spacecraft"]     = Body::Spacecraft;
  108.     Classifications["invisible"]      = Body::Invisible;
  109.     Classifications["surfacefeature"] = Body::SurfaceFeature;
  110.     Classifications["component"]      = Body::Component;
  111.     Classifications["diffuse"]        = Body::Diffuse;
  112. }
  113. int GetClassificationId(const string& className)
  114. {
  115.     if (Classifications.empty())
  116.         InitializeClassifications();
  117.     ClassificationTable::iterator iter = Classifications.find(className);
  118.     if (iter == Classifications.end())
  119.         return Body::Unknown;
  120.     else
  121.         return iter->second;
  122. }
  123. //! Maximum depth permitted for nested frames.
  124. static unsigned int MaxFrameDepth = 50;
  125. static bool isFrameCircular(const ReferenceFrame& frame, ReferenceFrame::FrameType frameType)
  126. {
  127.     return frame.nestingDepth(MaxFrameDepth, frameType) > MaxFrameDepth;
  128. }
  129. static Location* CreateLocation(Hash* locationData,
  130.                                 Body* body)
  131. {
  132.     Location* location = new Location();
  133.     Vec3d longlat(0.0, 0.0, 0.0);
  134.     locationData->getVector("LongLat", longlat);
  135.     Vec3d position = body->planetocentricToCartesian(longlat.x,
  136.                                                      longlat.y,
  137.                                                      longlat.z);
  138.     location->setPosition(Vec3f((float) position.x, (float) position.y, (float) position.z));
  139.     double size = 1.0;
  140.     locationData->getNumber("Size", size);
  141.     location->setSize((float) size);
  142.     double importance = -1.0;
  143.     locationData->getNumber("Importance", importance);
  144.     location->setImportance((float) importance);
  145.     string featureTypeName;
  146.     if (locationData->getString("Type", featureTypeName))
  147.         location->setFeatureType(Location::parseFeatureType(featureTypeName));
  148.     Color labelColor;
  149.     if (locationData->getColor("LabelColor", labelColor))
  150.     {
  151.         location->setLabelColor(labelColor);
  152.         location->setLabelColorOverridden(true);
  153.     }
  154.     return location;
  155. }
  156. static void FillinSurface(Hash* surfaceData,
  157.                           Surface* surface,
  158.                           const std::string& path)
  159. {
  160.     surfaceData->getColor("Color", surface->color);
  161.     // Haze is deprecated; used only in pre-OpenGL 2.0 render paths
  162.     Color hazeColor = surface->hazeColor;
  163.     float hazeDensity = hazeColor.alpha();
  164.     if (surfaceData->getColor("HazeColor", hazeColor) | surfaceData->getNumber("HazeDensity", hazeDensity))
  165.     {
  166.         surface->hazeColor = Color(hazeColor.red(), hazeColor.green(),
  167.                                    hazeColor.blue(), hazeDensity);
  168.     }
  169.     surfaceData->getColor("SpecularColor", surface->specularColor);
  170.     surfaceData->getNumber("SpecularPower", surface->specularPower);
  171.     surfaceData->getNumber("LunarLambert", surface->lunarLambert);
  172. #ifdef USE_HDR
  173.     surfaceData->getNumber("NightLightRadiance", surface->nightLightRadiance);
  174. #endif
  175.     string baseTexture;
  176.     string bumpTexture;
  177.     string nightTexture;
  178.     string specularTexture;
  179.     string normalTexture;
  180.     string overlayTexture;
  181.     bool applyBaseTexture = surfaceData->getString("Texture", baseTexture);
  182.     bool applyBumpMap = surfaceData->getString("BumpMap", bumpTexture);
  183.     bool applyNightMap = surfaceData->getString("NightTexture", nightTexture);
  184.     bool separateSpecular = surfaceData->getString("SpecularTexture",
  185.                                                    specularTexture);
  186.     bool applyNormalMap = surfaceData->getString("NormalMap", normalTexture);
  187.     bool applyOverlay = surfaceData->getString("OverlayTexture",
  188.                                                overlayTexture);
  189.     unsigned int baseFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
  190.     unsigned int bumpFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
  191.     unsigned int nightFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
  192.     unsigned int specularFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
  193.     float bumpHeight = 2.5f;
  194.     surfaceData->getNumber("BumpHeight", bumpHeight);
  195.     bool blendTexture = false;
  196.     surfaceData->getBoolean("BlendTexture", blendTexture);
  197.     bool emissive = false;
  198.     surfaceData->getBoolean("Emissive", emissive);
  199.     bool compressTexture = false;
  200.     surfaceData->getBoolean("CompressTexture", compressTexture);
  201.     if (compressTexture)
  202.         baseFlags |= TextureInfo::CompressTexture;
  203.     if (blendTexture)
  204.         surface->appearanceFlags |= Surface::BlendTexture;
  205.     if (emissive)
  206.         surface->appearanceFlags |= Surface::Emissive;
  207.     if (applyBaseTexture)
  208.         surface->appearanceFlags |= Surface::ApplyBaseTexture;
  209.     if (applyBumpMap || applyNormalMap)
  210.         surface->appearanceFlags |= Surface::ApplyBumpMap;
  211.     if (applyNightMap)
  212.         surface->appearanceFlags |= Surface::ApplyNightMap;
  213.     if (separateSpecular)
  214.         surface->appearanceFlags |= Surface::SeparateSpecularMap;
  215.     if (applyOverlay)
  216.         surface->appearanceFlags |= Surface::ApplyOverlay;
  217.     if (surface->specularColor != Color(0.0f, 0.0f, 0.0f))
  218.         surface->appearanceFlags |= Surface::SpecularReflection;
  219.     if (applyBaseTexture)
  220.         surface->baseTexture.setTexture(baseTexture, path, baseFlags);
  221.     if (applyNightMap)
  222.         surface->nightTexture.setTexture(nightTexture, path, nightFlags);
  223.     if (separateSpecular)
  224.         surface->specularTexture.setTexture(specularTexture, path, specularFlags);
  225.     // If both are present, NormalMap overrides BumpMap
  226.     if (applyNormalMap)
  227.         surface->bumpTexture.setTexture(normalTexture, path, bumpFlags);
  228.     else if (applyBumpMap)
  229.         surface->bumpTexture.setTexture(bumpTexture, path, bumpHeight, bumpFlags);
  230.     if (applyOverlay)
  231.         surface->overlayTexture.setTexture(overlayTexture, path, baseFlags);
  232. }
  233. static Selection GetParentObject(PlanetarySystem* system)
  234. {
  235.     Selection parent;
  236.     Body* primary = system->getPrimaryBody();
  237.     if (primary != NULL)
  238.         parent = Selection(primary);
  239.     else
  240.         parent = Selection(system->getStar());
  241.     return parent;
  242. }
  243. TimelinePhase* CreateTimelinePhase(Body* body,
  244.                                    Universe& universe,
  245.                                    Hash* phaseData,
  246.                                    const string& path,
  247.                                    ReferenceFrame* defaultOrbitFrame,
  248.                                    ReferenceFrame* defaultBodyFrame,
  249.                                    bool isFirstPhase,
  250.                                    bool isLastPhase,
  251.                                    double previousPhaseEnd)
  252. {
  253.     double beginning = previousPhaseEnd;
  254.     double ending = numeric_limits<double>::infinity();
  255.     // Beginning is optional for the first phase of a timeline, and not
  256.     // allowed for the other phases, where beginning is always the ending
  257.     // of the previous phase.
  258.     bool hasBeginning = ParseDate(phaseData, "Beginning", beginning);
  259.     if (!isFirstPhase && hasBeginning)
  260.     {
  261.         clog << "Error: Beginning can only be specified for initial phase of timeline.n";
  262.         return NULL;
  263.     }
  264.     // Ending is required for all phases except for the final one.
  265.     bool hasEnding = ParseDate(phaseData, "Ending", ending);
  266.     if (!isLastPhase && !hasEnding)
  267.     {
  268.         clog << "Error: Ending is required for all timeline phases other than the final one.n";
  269.         return NULL;
  270.     }
  271.     // Get the orbit reference frame.
  272.     ReferenceFrame* orbitFrame;
  273.     Value* frameValue = phaseData->getValue("OrbitFrame");
  274.     if (frameValue != NULL)
  275.     {
  276.         orbitFrame = CreateReferenceFrame(universe, frameValue, defaultOrbitFrame->getCenter(), body);
  277.         if (orbitFrame == NULL)
  278.         {
  279.             return NULL;
  280.         }
  281.     }
  282.     else
  283.     {
  284.         // No orbit frame specified; use the default frame.
  285.         orbitFrame = defaultOrbitFrame;
  286.     }
  287.     orbitFrame->addRef();
  288.     // Get the body reference frame
  289.     ReferenceFrame* bodyFrame;
  290.     Value* bodyFrameValue = phaseData->getValue("BodyFrame");
  291.     if (bodyFrameValue != NULL)
  292.     {
  293.         bodyFrame = CreateReferenceFrame(universe, bodyFrameValue, defaultBodyFrame->getCenter(), body);
  294.         if (bodyFrame == NULL)
  295.         {
  296.             orbitFrame->release();
  297.             return NULL;
  298.         }
  299.     }
  300.     else
  301.     {
  302.         // No body frame specified; use the default frame.
  303.         bodyFrame = defaultBodyFrame;
  304.     }
  305.     bodyFrame->addRef();
  306.     // Use planet units (AU for semimajor axis) if the center of the orbit 
  307.     // reference frame is a star.
  308.     bool usePlanetUnits = orbitFrame->getCenter().star() != NULL;
  309.     // Get the orbit
  310.     Orbit* orbit = CreateOrbit(orbitFrame->getCenter(), phaseData, path, usePlanetUnits);
  311.     if (!orbit)
  312.     {
  313.         clog << "Error: missing orbit in timeline phase.n";
  314.         bodyFrame->release();
  315.         orbitFrame->release();
  316.         return NULL;
  317.     }
  318.     // Get the rotation model
  319.     // TIMELINE-TODO: default rotation model is UniformRotation with a period
  320.     // equal to the orbital period. Should we do something else?
  321.     RotationModel* rotationModel = CreateRotationModel(phaseData, path, orbit->getPeriod());
  322.     if (!rotationModel)
  323.     {
  324.         // TODO: Should distinguish between a missing rotation model (where it's
  325.         // appropriate to use a default one) and a bad rotation model (where
  326.         // we should report an error.)
  327.         rotationModel = new ConstantOrientation(Quatd(1.0));
  328.     }
  329.     TimelinePhase* phase = TimelinePhase::CreateTimelinePhase(universe,
  330.                                                               body,
  331.                                                               beginning, ending,
  332.                                                               *orbitFrame,
  333.                                                               *orbit,
  334.                                                               *bodyFrame,
  335.                                                               *rotationModel);
  336.     // Frame ownership transfered to phase; release local references
  337.     orbitFrame->release();
  338.     bodyFrame->release();
  339.     return phase;
  340. }
  341. Timeline* CreateTimelineFromArray(Body* body,
  342.                                   Universe& universe,
  343.                                   Array* timelineArray,
  344.                                   const string& path,
  345.                                   ReferenceFrame* defaultOrbitFrame,
  346.                                   ReferenceFrame* defaultBodyFrame)
  347. {
  348.     Timeline* timeline = new Timeline();
  349.     double previousEnding = -numeric_limits<double>::infinity();
  350.     for (Array::const_iterator iter = timelineArray->begin(); iter != timelineArray->end(); iter++)
  351.     {
  352.         Hash* phaseData = (*iter)->getHash();
  353.         if (phaseData == NULL)
  354.         {
  355.             clog << "Error in timeline of '" << body->getName() << "': phase " << iter - timelineArray->begin() + 1 << " is not a property group.n";
  356.             delete timeline;
  357.             return NULL;
  358.         }
  359.         bool isFirstPhase = iter == timelineArray->begin();
  360.         bool isLastPhase = *iter == timelineArray->back();
  361.         TimelinePhase* phase = CreateTimelinePhase(body, universe, phaseData,
  362.                                                    path,
  363.                                                    defaultOrbitFrame,
  364.                                                    defaultBodyFrame,
  365.                                                    isFirstPhase, isLastPhase, previousEnding);
  366.         if (phase == NULL)
  367.         {
  368.             clog << "Error in timeline of '" << body->getName() << "', phase " << iter - timelineArray->begin() + 1 << endl;
  369.             delete timeline;
  370.             return NULL;
  371.         }
  372.         previousEnding = phase->endTime();
  373.         timeline->appendPhase(phase);
  374.     }
  375.     return timeline;
  376. }
  377. static bool CreateTimeline(Body* body,
  378.                            PlanetarySystem* system,
  379.                            Universe& universe,
  380.                            Hash* planetData,
  381.                            const string& path,
  382.                            Disposition disposition,
  383.                            BodyType bodyType)
  384. {
  385.     FrameTree* parentFrameTree = NULL;
  386.     Selection parentObject = GetParentObject(system);
  387.     bool orbitsPlanet = false;
  388.     if (parentObject.body())
  389.     {
  390.         parentFrameTree = parentObject.body()->getOrCreateFrameTree();
  391.         orbitsPlanet = true;
  392.     }
  393.     else if (parentObject.star())
  394.     {
  395.         SolarSystem* solarSystem = universe.getSolarSystem(parentObject.star());
  396.         if (solarSystem == NULL)
  397.             solarSystem = universe.createSolarSystem(parentObject.star());
  398.         parentFrameTree = solarSystem->getFrameTree();
  399.     }
  400.     else
  401.     {
  402.         // Bad orbit barycenter specified
  403.         return false;
  404.     }
  405.     ReferenceFrame* defaultOrbitFrame = NULL;
  406.     ReferenceFrame* defaultBodyFrame = NULL;
  407.     if (bodyType == SurfaceObject)
  408.     {
  409.         defaultOrbitFrame = new BodyFixedFrame(parentObject, parentObject);
  410.         defaultBodyFrame = CreateTopocentricFrame(parentObject, parentObject, Selection(body));
  411.         defaultOrbitFrame->addRef();
  412.         defaultBodyFrame->addRef();
  413.     }
  414.     else
  415.     {
  416.         defaultOrbitFrame = parentFrameTree->getDefaultReferenceFrame();
  417.         defaultBodyFrame = parentFrameTree->getDefaultReferenceFrame();
  418.     }
  419.     
  420.     // If there's an explicit timeline definition, parse that. Otherwise, we'll do
  421.     // things the old way.
  422.     Value* value = planetData->getValue("Timeline");
  423.     if (value != NULL)
  424.     {
  425.         if (value->getType() != Value::ArrayType)
  426.         {
  427.             clog << "Error: Timeline must be an arrayn";
  428.             return false;
  429.         }
  430.         Timeline* timeline = CreateTimelineFromArray(body, universe, value->getArray(), path,
  431.                                                      defaultOrbitFrame, defaultBodyFrame);
  432.         if (timeline == NULL)
  433.         {
  434.             return false;
  435.         }
  436.         else
  437.         {
  438.             body->setTimeline(timeline);
  439.             return true;
  440.         }
  441.     }
  442.     // Information required for the object timeline.
  443.     ReferenceFrame* orbitFrame   = NULL;
  444.     ReferenceFrame* bodyFrame    = NULL;
  445.     Orbit* orbit                 = NULL;
  446.     RotationModel* rotationModel = NULL;
  447.     double beginning             = -numeric_limits<double>::infinity();
  448.     double ending                =  numeric_limits<double>::infinity();
  449.     // If any new timeline values are specified, we need to overrideOldTimeline will
  450.     // be set to true.
  451.     bool overrideOldTimeline = false;
  452.     // The interaction of Modify with timelines is slightly complicated. If the timeline
  453.     // is specified by putting the OrbitFrame, Orbit, BodyFrame, or RotationModel directly
  454.     // in the object definition (i.e. not inside a Timeline structure), it will completely
  455.     // replace the previous timeline if it contained more than one phase. Otherwise, the
  456.     // properties of the single phase will be modified individually, for compatibility with
  457.     // Celestia versions 1.5.0 and earlier.
  458.     if (disposition == ModifyObject)
  459.     {
  460.         const Timeline* timeline = body->getTimeline();
  461.         if (timeline->phaseCount() == 1)
  462.         {
  463.             const TimelinePhase* phase = timeline->getPhase(0);
  464.             orbitFrame    = phase->orbitFrame();
  465.             bodyFrame     = phase->bodyFrame();
  466.             orbit         = phase->orbit();
  467.             rotationModel = phase->rotationModel();
  468.             beginning     = phase->startTime();
  469.             ending        = phase->endTime();
  470.         }
  471.     }
  472.     // Get the object's orbit reference frame.
  473.     bool newOrbitFrame = false;
  474.     Value* frameValue = planetData->getValue("OrbitFrame");
  475.     if (frameValue != NULL)
  476.     {
  477.         ReferenceFrame* frame = CreateReferenceFrame(universe, frameValue, parentObject, body);
  478.         if (frame != NULL)
  479.         {
  480.             orbitFrame = frame;
  481.             newOrbitFrame = true;
  482.             overrideOldTimeline = true;
  483.         }
  484.     }
  485.     // Get the object's body frame.
  486.     bool newBodyFrame = false;
  487.     Value* bodyFrameValue = planetData->getValue("BodyFrame");
  488.     if (bodyFrameValue != NULL)
  489.     {
  490.         ReferenceFrame* frame = CreateReferenceFrame(universe, bodyFrameValue, parentObject, body);
  491.         if (frame != NULL)
  492.         {
  493.             bodyFrame = frame;
  494.             newBodyFrame = true;
  495.             overrideOldTimeline = true;
  496.         }
  497.     }
  498.     // If no orbit or body frame was specified, use the default ones
  499.     if (orbitFrame == NULL)
  500.         orbitFrame = defaultOrbitFrame;
  501.     if (bodyFrame == NULL)
  502.         bodyFrame = defaultBodyFrame;
  503.     // If the center of the is a star, orbital element units are
  504.     // in AU; otherwise, use kilometers.
  505.     if (orbitFrame->getCenter().star() != NULL)
  506.         orbitsPlanet = false;
  507.     else
  508.         orbitsPlanet = true;
  509.     Orbit* newOrbit = CreateOrbit(orbitFrame->getCenter(), planetData, path, !orbitsPlanet);
  510.     if (newOrbit == NULL && orbit == NULL)
  511.     {
  512.         clog << "No valid orbit specified for object '" << body->getName() << "'. Skipping.n";
  513.         return false;
  514.     }
  515.     // If a new orbit was given, override any old orbit
  516.     if (newOrbit != NULL)
  517.     {
  518.         orbit = newOrbit;
  519.         overrideOldTimeline = true;
  520.     }
  521.     // Get the rotation model for this body
  522.     double syncRotationPeriod = orbit->getPeriod();
  523.     RotationModel* newRotationModel = CreateRotationModel(planetData, path, syncRotationPeriod);
  524.     // If a new rotation model was given, override the old one
  525.     if (newRotationModel != NULL)
  526.     {
  527.         rotationModel = newRotationModel;
  528.         overrideOldTimeline = true;
  529.     }
  530.     // If there was no rotation model specified, nor a previous rotation model to
  531.     // override, create the default one.
  532.     if (rotationModel == NULL)
  533.     {
  534.         // If no rotation model is provided, use a default rotation model--
  535.         // a uniform rotation that's synchronous with the orbit (appropriate
  536.         // for nearly all natural satellites in the solar system.)
  537.         rotationModel = CreateDefaultRotationModel(syncRotationPeriod);
  538.     }
  539.     
  540.     if (ParseDate(planetData, "Beginning", beginning))
  541.         overrideOldTimeline = true;
  542.     if (ParseDate(planetData, "Ending", ending))
  543.         overrideOldTimeline = true;
  544.     // Something went wrong if the disposition isn't modify and no timeline
  545.     // is to be created.
  546.     assert(disposition == ModifyObject || overrideOldTimeline);
  547.     if (overrideOldTimeline)
  548.     {
  549.         if (beginning >= ending)
  550.         {
  551.             clog << "Beginning time must be before Ending time.n";
  552.             return false;
  553.         }
  554.         // We finally have an orbit, rotation model, frames, and time range. Create
  555.         // the object timeline.
  556.         TimelinePhase* phase = TimelinePhase::CreateTimelinePhase(universe,
  557.                                                                   body,
  558.                                                                   beginning, ending,
  559.                                                                   *orbitFrame,
  560.                                                                   *orbit,
  561.                                                                   *bodyFrame,
  562.                                                                   *rotationModel);
  563.         // We've already checked that beginning < ending; nothing else should go
  564.         // wrong during the creation of a TimelinePhase.
  565.         assert(phase != NULL);
  566.         if (phase == NULL)
  567.         {
  568.             clog << "Internal error creating TimelinePhase.n";
  569.             return false;
  570.         }
  571.         Timeline* timeline = new Timeline();
  572.         timeline->appendPhase(phase);
  573.         body->setTimeline(timeline);
  574.         // Check for circular references in frames; this can only be done once the timeline
  575.         // has actually been set.
  576.         // TIMELINE-TODO: This check is not comprehensive; it won't find recursion in
  577.         // multiphase timelines.
  578.         if (newOrbitFrame && isFrameCircular(*body->getOrbitFrame(0.0), ReferenceFrame::PositionFrame))
  579.         {
  580.             clog << "Orbit frame for " << body->getName() << " is nested too deep (probably circular)n";
  581.             return false;
  582.         }
  583.         if (newBodyFrame && isFrameCircular(*body->getBodyFrame(0.0), ReferenceFrame::OrientationFrame))
  584.         {
  585.             clog << "Body frame for " << body->getName() << " is nested too deep (probably circular)n";
  586.             return false;
  587.         }
  588.     }
  589.     return true;
  590. }
  591. // Create a body (planet, moon, spacecraft, etc.) using the values from a
  592. // property list. The usePlanetsUnits flags specifies whether period and
  593. // semi-major axis are in years and AU rather than days and kilometers.
  594. static Body* CreateBody(const string& name,
  595.                         PlanetarySystem* system,
  596.                         Universe& universe,
  597.                         Body* existingBody,
  598.                         Hash* planetData,
  599.                         const string& path,
  600.                         Disposition disposition,
  601.                         BodyType bodyType)
  602. {
  603.     Body* body = NULL;
  604.     if (disposition == ModifyObject || disposition == ReplaceObject)
  605.     {
  606.         body = existingBody;
  607.     }
  608.     if (body == NULL)
  609.     {
  610.         body = new Body(system, name);
  611.         // If the body doesn't exist, always treat the disposition as 'Add'
  612.         disposition = AddObject;
  613.         
  614.         // Set the default classification for new objects based on the body type.
  615.         // This may be overridden by the Class property.
  616.         if (bodyType == SurfaceObject)
  617.         {
  618.             body->setClassification(Body::SurfaceFeature);
  619.         }
  620.     }
  621.     if (!CreateTimeline(body, system, universe, planetData, path, disposition, bodyType))
  622.     {
  623.         // No valid timeline given; give up.
  624.         if (body != existingBody)
  625.             delete body;
  626.         return NULL;
  627.     }
  628.     // Three values control the shape and size of an ellipsoidal object:
  629.     // semiAxes, radius, and oblateness. It is an error if neither the
  630.     // radius nor semiaxes are set. If both are set, the radius is
  631.     // multipled by each of the specified semiaxis to give the shape of
  632.     // the body ellipsoid. Oblateness is ignored if semiaxes are provided;
  633.     // otherwise, the ellipsoid has semiaxes: ( radius, radius, 1-radius ).
  634.     // These rather complex rules exist to maintain backward compatibility.
  635.     //
  636.     // If the body also has a mesh, it is always scaled in x, y, and z by
  637.     // the maximum semiaxis, never anisotropically.
  638.     double radius = (double) body->getRadius();
  639.     bool radiusSpecified = false;
  640.     if (planetData->getNumber("Radius", radius))
  641.     {
  642.         body->setSemiAxes(Vec3f((float) radius, (float) radius, (float) radius));
  643.         radiusSpecified = true;
  644.     }
  645.     Vec3d semiAxes;
  646.     if (planetData->getVector("SemiAxes", semiAxes))
  647.     {
  648.         if (radiusSpecified)
  649.             semiAxes *= radius;
  650.         // Swap y and z to match internal coordinate system
  651.         body->setSemiAxes(Vec3f((float) semiAxes.x, (float) semiAxes.z, (float) semiAxes.y));
  652.     }
  653.     else
  654.     {
  655.         double oblateness = 0.0;
  656.         if (planetData->getNumber("Oblateness", oblateness))
  657.         {
  658.             body->setSemiAxes((float) body->getRadius() * Vec3f(1.0f, 1.0f - (float) oblateness, 1.0f));
  659.         }
  660.     }
  661.     int classification = body->getClassification();
  662.     string classificationName;
  663.     if (planetData->getString("Class", classificationName))
  664.         classification = GetClassificationId(classificationName);
  665.     if (classification == Body::Unknown)
  666.     {
  667.         // Try to guess the type
  668.         if (system->getPrimaryBody() != NULL)
  669.         {
  670.             if(radius > 0.1)
  671.                 classification = Body::Moon;
  672.             else
  673.                 classification = Body::Spacecraft;
  674.         }
  675.         else
  676.         {
  677.             if (radius < 1000.0)
  678.                 classification = Body::Asteroid;
  679.             else
  680.                 classification = Body::Planet;
  681.         }
  682.     }
  683.     body->setClassification(classification);
  684.     if (classification == Body::Invisible)
  685.         body->setVisible(false);
  686.     // Set default properties for the object based on its classification
  687.     if (classification & CLASSES_INVISIBLE_AS_POINT)
  688.         body->setVisibleAsPoint(false);
  689.     if ((classification & CLASSES_SECONDARY_ILLUMINATOR) == 0)
  690.         body->setSecondaryIlluminator(false);
  691.     if (classification & CLASSES_UNCLICKABLE)
  692.         body->setClickable(false);
  693.     string infoURL;
  694.     if (planetData->getString("InfoURL", infoURL))
  695.     {
  696.         if (infoURL.find(':') == string::npos)
  697.         {
  698.             // Relative URL, the base directory is the current one,
  699.             // not the main installation directory
  700.             if (path[1] == ':')
  701.                 // Absolute Windows path, file:/// is required
  702.                 infoURL = "file:///" + path + "/" + infoURL;
  703.             else if (!path.empty())
  704.                 infoURL = path + "/" + infoURL;
  705.         }
  706.         body->setInfoURL(infoURL);
  707.     }
  708.     double albedo = 0.5;
  709.     if (planetData->getNumber("Albedo", albedo))
  710.         body->setAlbedo((float) albedo);
  711.     double mass = 0.0;
  712.     if (planetData->getNumber("Mass", mass))
  713.         body->setMass((float) mass);
  714.     Quatf orientation;
  715.     if (planetData->getRotation("Orientation", orientation))
  716.         body->setOrientation(orientation);
  717.     Surface surface;
  718.     if (disposition == ModifyObject)
  719.     {
  720.         surface = body->getSurface();
  721.     }
  722.     else
  723.     {
  724.         surface.color = Color(1.0f, 1.0f, 1.0f);
  725.         surface.hazeColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
  726.     }
  727.     FillinSurface(planetData, &surface, path);
  728.     body->setSurface(surface);
  729.     {
  730.         string geometry("");
  731.         if (planetData->getString("Mesh", geometry))
  732.         {
  733.             Vec3f geometryCenter(0.0f, 0.0f, 0.0f);
  734.             if (planetData->getVector("MeshCenter", geometryCenter))
  735.             {
  736.                 // TODO: Adjust bounding radius if model center isn't
  737.                 // (0.0f, 0.0f, 0.0f)
  738.             }
  739.             bool isNormalized = true;
  740.             planetData->getBoolean("NormalizeMesh", isNormalized);
  741.             float geometryScale = 1.0f;
  742.             planetData->getNumber("MeshScale", geometryScale);
  743.             ResourceHandle geometryHandle = GetGeometryManager()->getHandle(GeometryInfo(geometry, path, geometryCenter, 1.0f, isNormalized));
  744.             body->setGeometry(geometryHandle);
  745.             body->setGeometryScale(geometryScale);
  746.         }
  747.     }
  748.     // Read the atmosphere
  749.     {
  750.         Value* atmosDataValue = planetData->getValue("Atmosphere");
  751.         if (atmosDataValue != NULL)
  752.         {
  753.             if (atmosDataValue->getType() != Value::HashType)
  754.             {
  755.                 cout << "ReadSolarSystem: Atmosphere must be an assoc array.n";
  756.             }
  757.             else
  758.             {
  759.                 Hash* atmosData = atmosDataValue->getHash();
  760.                 assert(atmosData != NULL);
  761.                 Atmosphere* atmosphere = NULL;
  762.                 if (disposition == ModifyObject)
  763.                 {
  764.                     atmosphere = body->getAtmosphere();
  765.                     if (atmosphere == NULL)
  766.                     {
  767.                         Atmosphere atm;
  768.                         body->setAtmosphere(atm);
  769.                         atmosphere = body->getAtmosphere();
  770.                     }
  771.                 }
  772.                 else
  773.                 {
  774.                     atmosphere = new Atmosphere();
  775.                 }
  776.                 atmosData->getNumber("Height", atmosphere->height);
  777.                 atmosData->getColor("Lower", atmosphere->lowerColor);
  778.                 atmosData->getColor("Upper", atmosphere->upperColor);
  779.                 atmosData->getColor("Sky", atmosphere->skyColor);
  780.                 atmosData->getColor("Sunset", atmosphere->sunsetColor);
  781.                 atmosData->getNumber("Mie", atmosphere->mieCoeff);
  782.                 atmosData->getNumber("MieScaleHeight", atmosphere->mieScaleHeight);
  783.                 atmosData->getNumber("MieAsymmetry", atmosphere->miePhaseAsymmetry);
  784.                 atmosData->getVector("Rayleigh", atmosphere->rayleighCoeff);
  785.                 //atmosData->getNumber("RayleighScaleHeight", atmosphere->rayleighScaleHeight);
  786.                 atmosData->getVector("Absorption", atmosphere->absorptionCoeff);
  787.                 // Get the cloud map settings
  788.                 atmosData->getNumber("CloudHeight", atmosphere->cloudHeight);
  789.                 if (atmosData->getNumber("CloudSpeed", atmosphere->cloudSpeed))
  790.                     atmosphere->cloudSpeed = degToRad(atmosphere->cloudSpeed);
  791.                 string cloudTexture;
  792.                 if (atmosData->getString("CloudMap", cloudTexture))
  793.                 {
  794.                     atmosphere->cloudTexture.setTexture(cloudTexture,
  795.                                                         path,
  796.                                                         TextureInfo::WrapTexture);
  797.                 }
  798.                 string cloudNormalMap;
  799.                 if (atmosData->getString("CloudNormalMap", cloudNormalMap))
  800.                 {
  801.                     atmosphere->cloudNormalMap.setTexture(cloudNormalMap,
  802.                                                            path,
  803.                                                            TextureInfo::WrapTexture);
  804.                 }
  805.                 double cloudShadowDepth = 0.0;
  806.                 if (atmosData->getNumber("CloudShadowDepth", cloudShadowDepth))
  807.                 {
  808.                     cloudShadowDepth = max(0.0, min(1.0, cloudShadowDepth));  // clamp to [0, 1]
  809.                     atmosphere->cloudShadowDepth = (float) cloudShadowDepth;
  810.                 }
  811.                 body->setAtmosphere(*atmosphere);
  812.                 if (disposition != ModifyObject)
  813.                     delete atmosphere;
  814.             }
  815.         }
  816.     }
  817.     // Read the ring system
  818.     {
  819.         Value* ringsDataValue = planetData->getValue("Rings");
  820.         if (ringsDataValue != NULL)
  821.         {
  822.             if (ringsDataValue->getType() != Value::HashType)
  823.             {
  824.                 cout << "ReadSolarSystem: Rings must be an assoc array.n";
  825.             }
  826.             else
  827.             {
  828.                 Hash* ringsData = ringsDataValue->getHash();
  829.                 // ASSERT(ringsData != NULL);
  830.                 RingSystem rings(0.0f, 0.0f);
  831.                 if (body->getRings() != NULL)
  832.                     rings = *body->getRings();
  833.                 double inner = 0.0, outer = 0.0;
  834.                 if (ringsData->getNumber("Inner", inner))
  835.                     rings.innerRadius = (float) inner;
  836.                 if (ringsData->getNumber("Outer", outer))
  837.                     rings.outerRadius = (float) outer;
  838.                 Color color(1.0f, 1.0f, 1.0f);
  839.                 if (ringsData->getColor("Color", color))
  840.                     rings.color = color;
  841.                 string textureName;
  842.                 if (ringsData->getString("Texture", textureName))
  843.                     rings.texture = MultiResTexture(textureName, path);
  844.                 body->setRings(rings);
  845.             }
  846.         }
  847.     }
  848.     
  849.     bool clickable = true;
  850.     if (planetData->getBoolean("Clickable", clickable))
  851.     {
  852.         body->setClickable(clickable);
  853.     }
  854.     bool visible = true;
  855.     if (planetData->getBoolean("Visible", visible))
  856.     {
  857.         body->setVisible(visible);
  858.     }
  859.     Color orbitColor;
  860.     if (planetData->getColor("OrbitColor", orbitColor))
  861.     {
  862.         body->setOrbitColorOverridden(true);
  863.         body->setOrbitColor(orbitColor);
  864.     }
  865.     return body;
  866. }
  867. // Create a barycenter object using the values from a hash
  868. static Body* CreateReferencePoint(const string& name,
  869.                                   PlanetarySystem* system,
  870.                                   Universe& universe,
  871.                                   Body* existingBody,
  872.                                   Hash* refPointData,
  873.                                   const string& path,
  874.                                   Disposition disposition)
  875. {
  876.     Body* body = NULL;
  877.     if (disposition == ModifyObject || disposition == ReplaceObject)
  878.     {
  879.         body = existingBody;
  880.     }
  881.     if (body == NULL)
  882.     {
  883.         body = new Body(system, name);
  884.         // If the point doesn't exist, always treat the disposition as 'Add'
  885.         disposition = AddObject;
  886.     }
  887.     body->setSemiAxes(Vec3f(1.0f, 1.0f, 1.0f));
  888.     body->setClassification(Body::Invisible);
  889.     body->setVisible(false);
  890.     body->setVisibleAsPoint(false);
  891.     body->setClickable(false);
  892.     if (!CreateTimeline(body, system, universe, refPointData, path, disposition, ReferencePoint))
  893.     {
  894.         // No valid timeline given; give up.
  895.         if (body != existingBody)
  896.             delete body;
  897.         return NULL;
  898.     }
  899.     // Reference points can be marked visible; no geometry is shown, but the label and orbit
  900.     // will be.
  901.     bool visible = false;
  902.     if (refPointData->getBoolean("Visible", visible))
  903.     {
  904.         body->setVisible(visible);
  905.     }
  906.     bool clickable = false;
  907.     if (refPointData->getBoolean("Clickable", clickable))
  908.     {
  909.         body->setClickable(clickable);
  910.     }
  911.     return body;
  912. }
  913. bool LoadSolarSystemObjects(istream& in,
  914.                             Universe& universe,
  915.                             const std::string& directory)
  916. {
  917.     Tokenizer tokenizer(&in);
  918.     Parser parser(&tokenizer);
  919.     while (tokenizer.nextToken() != Tokenizer::TokenEnd)
  920.     {
  921.         // Read the disposition; if none is specified, the default is Add.
  922.         Disposition disposition = AddObject;
  923.         if (tokenizer.getTokenType() == Tokenizer::TokenName)
  924.         {
  925.             if (tokenizer.getNameValue() == "Add")
  926.             {
  927.                 disposition = AddObject;
  928.                 tokenizer.nextToken();
  929.             }
  930.             else if (tokenizer.getNameValue() == "Replace")
  931.             {
  932.                 disposition = ReplaceObject;
  933.                 tokenizer.nextToken();
  934.             }
  935.             else if (tokenizer.getNameValue() == "Modify")
  936.             {
  937.                 disposition = ModifyObject;
  938.                 tokenizer.nextToken();
  939.             }
  940.         }
  941.         // Read the item type; if none is specified the default is Body
  942.         string itemType("Body");
  943.         if (tokenizer.getTokenType() == Tokenizer::TokenName)
  944.         {
  945.             itemType = tokenizer.getNameValue();
  946.             tokenizer.nextToken();
  947.         }
  948.         if (tokenizer.getTokenType() != Tokenizer::TokenString)
  949.         {
  950.             sscError(tokenizer, "object name expected");
  951.             return false;
  952.         }
  953.         
  954.         // The name list is a string with zero more names. Multiple names are
  955.         // delimited by colons.
  956.         string nameList = tokenizer.getStringValue().c_str();
  957.         if (tokenizer.nextToken() != Tokenizer::TokenString)
  958.         {
  959.             sscError(tokenizer, "bad parent object name");
  960.             return false;
  961.         }
  962.         string parentName = tokenizer.getStringValue().c_str();
  963.         Value* objectDataValue = parser.readValue();
  964.         if (objectDataValue == NULL)
  965.         {
  966.             sscError(tokenizer, "bad object definition");
  967.             return false;
  968.         }
  969.         if (objectDataValue->getType() != Value::HashType)
  970.         {
  971.             sscError(tokenizer, "{ expected");
  972.             delete objectDataValue;
  973.             return false;
  974.         }
  975.         Hash* objectData = objectDataValue->getHash();
  976.         Selection parent = universe.findPath(parentName, NULL, 0);
  977.         PlanetarySystem* parentSystem = NULL;
  978.         
  979.         vector<string> names;
  980.         // Iterate through the string for names delimited
  981.         // by ':', and insert them into the name list.
  982.         if (nameList.empty())
  983.         {
  984.             names.push_back("");
  985.         }
  986.         else
  987.         {
  988.             string::size_type startPos   = 0;
  989.             while (startPos != string::npos)
  990.             {
  991.                 string::size_type next    = nameList.find(':', startPos);
  992.                 string::size_type length  = string::npos;
  993.                 if (next != string::npos)
  994.                 {
  995.                     length   = next - startPos;
  996.                     ++next;
  997.                 }
  998.                 names.push_back(nameList.substr(startPos, length));
  999.                 startPos   = next;
  1000.             }
  1001.         }
  1002.         string primaryName = names.front();
  1003.         BodyType bodyType = UnknownBodyType;
  1004.         if (itemType == "Body")
  1005.             bodyType = NormalBody;
  1006.         else if (itemType == "ReferencePoint")
  1007.             bodyType = ReferencePoint;
  1008.         else if (itemType == "SurfaceObject")
  1009.             bodyType = SurfaceObject;
  1010.         
  1011.         if (bodyType != UnknownBodyType)
  1012.         {
  1013.             //bool orbitsPlanet = false;
  1014.             if (parent.star() != NULL)
  1015.             {
  1016.                 SolarSystem* solarSystem = universe.getSolarSystem(parent.star());
  1017.                 if (solarSystem == NULL)
  1018.                 {
  1019.                     // No solar system defined for this star yet, so we need
  1020.                     // to create it.
  1021.                     solarSystem = universe.createSolarSystem(parent.star());
  1022.                 }
  1023.                 parentSystem = solarSystem->getPlanets();
  1024.             }
  1025.             else if (parent.body() != NULL)
  1026.             {
  1027.                 // Parent is a planet or moon
  1028.                 parentSystem = parent.body()->getSatellites();
  1029.                 if (parentSystem == NULL)
  1030.                 {
  1031.                     // If the planet doesn't already have any satellites, we
  1032.                     // have to create a new planetary system for it.
  1033.                     parentSystem = new PlanetarySystem(parent.body());
  1034.                     parent.body()->setSatellites(parentSystem);
  1035.                 }
  1036.                 //orbitsPlanet = true;
  1037.             }
  1038.             else
  1039.             {
  1040.                 errorMessagePrelude(tokenizer);
  1041.                 cerr << _("parent body '") << parentName << _("' of '") << primaryName << _("' not found.") << endl;
  1042.             }
  1043.             if (parentSystem != NULL)
  1044.             {
  1045.                 Body* existingBody = parentSystem->find(primaryName);
  1046.                 if (existingBody)
  1047.                 {
  1048.                     if (disposition == AddObject)
  1049.                     {
  1050.                         errorMessagePrelude(tokenizer);
  1051.                         cerr << _("warning duplicate definition of ") <<
  1052.                             parentName << " " <<  primaryName << 'n';
  1053.                     }
  1054.                     else if (disposition == ReplaceObject)
  1055.                     {
  1056.                         existingBody->setDefaultProperties();
  1057.                     }
  1058.                 }
  1059.                 Body* body;
  1060.                 if (bodyType == ReferencePoint)
  1061.                     body = CreateReferencePoint(primaryName, parentSystem, universe, existingBody, objectData, directory, disposition);
  1062.                 else
  1063.                     body = CreateBody(primaryName, parentSystem, universe, existingBody, objectData, directory, disposition, bodyType);
  1064.                 
  1065.                 if (body != NULL && disposition == AddObject)
  1066.                 {
  1067.                     vector<string>::const_iterator iter = names.begin();
  1068.                     iter++;
  1069.                     while (iter != names.end())
  1070.                     {
  1071.                         body->addAlias(*iter);
  1072.                         iter++;
  1073.                     }
  1074.                 }
  1075.             }
  1076.         }
  1077.         else if (itemType == "AltSurface")
  1078.         {
  1079.             Surface* surface = new Surface();
  1080.             surface->color = Color(1.0f, 1.0f, 1.0f);
  1081.             surface->hazeColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
  1082.             FillinSurface(objectData, surface, directory);
  1083.             if (surface != NULL && parent.body() != NULL)
  1084.                 parent.body()->addAlternateSurface(primaryName, surface);
  1085.             else
  1086.                 sscError(tokenizer, _("bad alternate surface"));
  1087.         }
  1088.         else if (itemType == "Location")
  1089.         {
  1090.             if (parent.body() != NULL)
  1091.             {
  1092.                 Location* location = CreateLocation(objectData, parent.body());
  1093.                 if (location != NULL)
  1094.                 {
  1095.                     location->setName(primaryName);
  1096.                     parent.body()->addLocation(location);
  1097.                 }
  1098.                 else
  1099.                 {
  1100.                     sscError(tokenizer, _("bad location"));
  1101.                 }
  1102.             }
  1103.             else
  1104.             {
  1105.                 errorMessagePrelude(tokenizer);
  1106.                 cerr << _("parent body '") << parentName << _("' of '") << primaryName << _("' not found.n");
  1107.             }
  1108.         }
  1109.         delete objectDataValue;
  1110.     }
  1111.     // TODO: Return some notification if there's an error parsing the file
  1112.     return true;
  1113. }
  1114. SolarSystem::SolarSystem(Star* _star) : 
  1115.     star(_star),
  1116.     planets(NULL),
  1117.     frameTree(NULL)
  1118. {
  1119.     planets = new PlanetarySystem(star);
  1120.     frameTree = new FrameTree(star);
  1121. }
  1122. Star* SolarSystem::getStar() const
  1123. {
  1124.     return star;
  1125. }
  1126. Point3f SolarSystem::getCenter() const
  1127. {
  1128.     // TODO: This is a very simple method at the moment, but it will get
  1129.     // more complex when planets around multistar systems are supported
  1130.     // where the planets may orbit the center of mass of two stars.
  1131.     return star->getPosition();
  1132. }
  1133. PlanetarySystem* SolarSystem::getPlanets() const
  1134. {
  1135.     return planets;
  1136. }
  1137. FrameTree* SolarSystem::getFrameTree() const
  1138. {
  1139.     return frameTree;
  1140. }