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

OpenGL

开发平台:

Visual C++

  1. // solarsysxml.cpp
  2. //
  3. // Copyright (C) 2001 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 <cassert>
  10. #include <libxml/parser.h>
  11. #include <libxml/parserInternals.h>
  12. #include <libxml/SAX.h>
  13. #include <celutil/debug.h>
  14. #include <celmath/mathlib.h>
  15. #include "astro.h"
  16. #include "customorbit.h"
  17. #include "texmanager.h"
  18. #include "meshmanager.h"
  19. #include "solarsysxml.h"
  20. using namespace std;
  21. struct UnitDefinition
  22. {
  23.     char* name;
  24.     double conversion;
  25. };
  26. UnitDefinition distanceUnits[] =
  27. {
  28.     { "km", 1.0 },
  29.     { "m",  0.001 },
  30.     { "au", 149597870.7 },
  31.     { "ly", 9466411842000.0 },
  32. };
  33. UnitDefinition timeUnits[] =
  34. {
  35.     { "s", 1.0 },
  36.     { "m", 60.0 },
  37.     { "h", 3600.0 },
  38.     { "d", 86400.0 },
  39.     { "y", 86400.0 * 365.25 },
  40. };
  41. static xmlSAXHandler emptySAXHandler =
  42. {
  43.     NULL, /* internalSubset */
  44.     NULL, /* isStandalone */
  45.     NULL, /* hasInternalSubset */
  46.     NULL, /* hasExternalSubset */
  47.     NULL, /* resolveEntity */
  48.     NULL, /* getEntity */
  49.     NULL, /* entityDecl */
  50.     NULL, /* notationDecl */
  51.     NULL, /* attributeDecl */
  52.     NULL, /* elementDecl */
  53.     NULL, /* unparsedEntityDecl */
  54.     NULL, /* setDocumentLocator */
  55.     NULL, /* startDocument */
  56.     NULL, /* endDocument */
  57.     NULL, /* startElement */
  58.     NULL, /* endElement */
  59.     NULL, /* reference */
  60.     NULL, /* characters */
  61.     NULL, /* ignorableWhitespace */
  62.     NULL, /* processingInstruction */
  63.     NULL, /* comment */
  64.     NULL, /* xmlParserWarning */
  65.     NULL, /* xmlParserError */
  66.     NULL, /* xmlParserError */
  67.     NULL, /* getParameterEntity */
  68.     NULL, /* cdataBlock; */
  69.     NULL,  /* externalSubset; */
  70.     1
  71. };
  72. static xmlSAXHandler saxHandler;
  73. enum ParserState {
  74.     StartState,
  75.     EndState,
  76.     BodyState,
  77.     SurfaceState,
  78.     AtmosphereState,
  79.     RingsState,
  80.     BodyLeafState,
  81.     SurfaceLeafState,
  82.     AtmosphereLeafState,
  83.     RingsLeafState,
  84.     ErrorState,
  85. };
  86. struct ParserContext
  87. {
  88.     ParserState state;
  89.     Body* body;
  90.     Universe* universe;
  91. };
  92. static bool matchName(const xmlChar* s, const char* name)
  93. {
  94.     while ((char) *s == *name)
  95.     {
  96.         if (*s == '')
  97.             return true;
  98.         s++;
  99.         name++;
  100.     }
  101.     return false;
  102. }
  103. static bool parseBoolean(const xmlChar* s, bool& b)
  104. {
  105.     if (matchName(s, "true") || matchName(s, "1") || matchName(s, "on"))
  106.     {
  107.         b = true;
  108.         return true;
  109.     }
  110.     else if (matchName(s, "false") || matchName(s, "0") || matchName(s, "off"))
  111.     {
  112.         b = false;
  113.         return true;
  114.     }
  115.     else
  116.     {
  117.         return false;
  118.     }
  119. }
  120. static bool parseNumber(const xmlChar* s, double& d)
  121. {
  122.     return sscanf((char*) s, "%lf", &d) == 1;
  123. }
  124. static bool parseNumber(const xmlChar* s, float& f)
  125. {
  126.     double d;
  127.     if (parseNumber(s, d))
  128.     {
  129.         f = (float) d;
  130.         return true;
  131.     }
  132.     else
  133.     {
  134.         return false;
  135.     }
  136. }
  137. static bool parseNumberUnits(const xmlChar* s,
  138.                              double& d,
  139.                              UnitDefinition* unitTable,
  140.                              int unitTableLength,
  141.                              char* defaultUnitName)
  142. {
  143.     char unitName[4];
  144.     double value;
  145.     int nMatched = sscanf(reinterpret_cast<const char*>(s),
  146.                           "%lf%3s", &value, unitName);
  147.     cout << "parseNumberUnits(" << reinterpret_cast<const char*>(s) << ")n";
  148.     if (nMatched == 1)
  149.     {
  150.         cout << "matched = 1: " << reinterpret_cast<const char*>(s) << 'n';
  151.         d = value;
  152.         return true;
  153.     }
  154.     else if (nMatched == 2)
  155.     {
  156.         cout << "matched = 2: " << reinterpret_cast<const char*>(s) << 'n';
  157.         // Found a value and units; multiply the value by a conversion
  158.         // factor to produce default units
  159.         UnitDefinition* defaultUnit = NULL;
  160.         UnitDefinition* unit = NULL;
  161.         int i;
  162.         // Get the default unit
  163.         for (i = 0; i < unitTableLength; i++)
  164.         {
  165.             if (strcmp(defaultUnitName, unitTable[i].name) == 0)
  166.             {
  167.                 defaultUnit = &unitTable[i];
  168.                 break;
  169.             }
  170.         }
  171.         assert(defaultUnit != NULL);
  172.         // Try and the match the unit name specified in the xml attribute
  173.         for (i = 0; i < unitTableLength; i++)
  174.         {
  175.             if (strcmp(unitName, unitTable[i].name) == 0)
  176.             {
  177.                 unit = &unitTable[i];
  178.                 break;
  179.             }
  180.         }
  181.         if (unit != NULL)
  182.         {
  183.             // Match found; perform the conversion
  184.             d = value * unit->conversion  / defaultUnit->conversion;
  185.             cout << "converting: " << value << unit->name << " = " << d << defaultUnit->name << "n";
  186.             return true;
  187.         }
  188.         else
  189.         {
  190.             return false;
  191.         }
  192.     }
  193.     else
  194.     {
  195.         // Error . . . we matched nothing.
  196.         return false;
  197.     }
  198. }
  199. static bool parseDistance(const xmlChar* s, double& d, char* defaultUnitName)
  200. {
  201.     return parseNumberUnits(s, d,
  202.                             distanceUnits,
  203.                             sizeof distanceUnits / sizeof distanceUnits[0],
  204.                             defaultUnitName);
  205. }
  206. static bool parseDistance(const xmlChar* s, float& f, char* defaultUnitName)
  207. {
  208.     double d;
  209.     if (parseDistance(s, d, defaultUnitName))
  210.     {
  211.         f = (float) d;
  212.         return true;
  213.     }
  214.     else
  215.     {
  216.         return false;
  217.     }
  218. }
  219. static bool parseAngle(const xmlChar* s, double& d)
  220. {
  221.     return parseNumber(s, d);
  222. }
  223. static bool parseAngle(const xmlChar* s, float& f)
  224. {
  225.     double d;
  226.     if (parseAngle(s, d))
  227.     {
  228.         f = (float) d;
  229.         return true;
  230.     }
  231.     else
  232.     {
  233.         return false;
  234.     }
  235. }
  236. static bool parseTime(const xmlChar* s, double& d, char* defaultUnitName)
  237. {
  238.     return parseNumberUnits(s, d,
  239.                             timeUnits,
  240.                             sizeof timeUnits / sizeof timeUnits[0],
  241.                             defaultUnitName);
  242. }
  243. static bool parseTime(const xmlChar* s, float& f, char* defaultUnitName)
  244. {
  245.     double d;
  246.     if (parseTime(s, d, defaultUnitName))
  247.     {
  248.         f = (float) d;
  249.         return true;
  250.     }
  251.     else
  252.     {
  253.         return false;
  254.     }
  255. }
  256. static bool parseEpoch(const xmlChar* s, double& d)
  257. {
  258.     if (matchName(s, "J2000"))
  259.     {
  260.         d = astro::J2000;
  261.         return true;
  262.     }
  263.     else
  264.     {
  265.         return parseNumber(s, d);
  266.     }
  267. }
  268. static int hexDigit(char c)
  269. {
  270.     // Assumes an ASCII character set . . .
  271.     if (c >= '0' && c <= '9')
  272.         return c - '0';
  273.     else if (c >= 'a' && c <= 'f')
  274.         return 10 + (c - 'a');
  275.     else if (c >= 'A' && c <= 'F')
  276.         return 10 + (c - 'A');
  277.     else
  278.         return 0; // bad digit
  279. }
  280. ostream& operator<<(ostream& out, const Color& c)
  281. {
  282.     cout << '[' << c.red() << ',' << c.green() << ',' << c.blue() << ']';
  283.     return cout;
  284. }
  285. static bool parseColor(const xmlChar* s, Color& c)
  286. {
  287.     const char* colorName = reinterpret_cast<const char*>(s);
  288.     char hexColor[7];
  289.     float r = 0.0f;
  290.     float g = 0.0f;
  291.     float b = 0.0f;
  292.     cout << "parsing color: " << colorName << 'n';
  293.     if (sscanf(colorName, " #%6[0-9a-fA-F] ", hexColor))
  294.     {
  295.         if (strlen(hexColor) == 6)
  296.         {
  297.             c = Color((hexDigit(hexColor[0]) * 16 +
  298.                        hexDigit(hexColor[1])) / 255.0f,
  299.                       (hexDigit(hexColor[2]) * 16 +
  300.                        hexDigit(hexColor[3])) / 255.0f,
  301.                       (hexDigit(hexColor[4]) * 16 +
  302.                        hexDigit(hexColor[5])) / 255.0f);
  303.             cout << "1: " << c << 'n';
  304.             return true;
  305.         }
  306.         else if (strlen(hexColor) == 3)
  307.         {
  308.             c = Color((hexDigit(hexColor[0]) * 17) / 255.0f,
  309.                       (hexDigit(hexColor[1]) * 17) / 255.0f,
  310.                       (hexDigit(hexColor[2]) * 17) / 255.0f);
  311.             cout << "2: " << c << 'n';
  312.             return true;
  313.         }
  314.         else
  315.         {
  316.             return false;
  317.         }
  318.     }
  319.     else if (sscanf(colorName, " rgb( %f , %f , %f ) ", &r, &g, &b) == 3)
  320.     {
  321.         c = Color(r / 255.0f, g / 255.0f, b / 255.0f);
  322.         cout << "3: " << c << 'n';
  323.         return true;
  324.     }
  325.     else if (sscanf(colorName, " rgb( %f%% , %f%% , %f%% ) ", &r, &g, &b) == 3)
  326.     {
  327.         c = Color(r / 100.0f, g / 100.0f, b / 100.0f);
  328.         cout << "4: " << c << 'n';
  329.         return true;
  330.     }
  331.     else
  332.     {
  333.         return false;
  334.     }
  335. }
  336. static bool createBody(ParserContext* ctx, const xmlChar** att)
  337. {
  338.     const xmlChar* name = NULL;
  339.     const xmlChar* parentName = NULL;
  340.     // Get the name and parent attributes
  341.     if (att != NULL)
  342.     {
  343.         for (int i = 0; att[i] != NULL; i += 2)
  344.         {
  345.             if (matchName(att[i], "name"))
  346.                 name = att[i + 1];
  347.             else if (matchName(att[i], "parent"))
  348.                 parentName = att[i + 1];
  349.         }
  350.     }
  351.     // Require that both are present
  352.     if (name == NULL)
  353.     {
  354.         return false;
  355.     }
  356.     else if (parentName == NULL)
  357.     {
  358.         return false;
  359.     }
  360.     bool orbitsPlanet = false;
  361.     Selection parent = ctx->universe->findPath(reinterpret_cast<const char*>(parentName), NULL, 0);
  362.     PlanetarySystem* parentSystem = NULL;
  363.     if (parent.star != NULL)
  364.     {
  365.         SolarSystem* solarSystem = ctx->universe->getSolarSystem(parent.star);
  366.         if (solarSystem == NULL)
  367.         {
  368.             // No solar system defined for this star yet, so we need
  369.             // to create it.
  370.             solarSystem = ctx->universe->createSolarSystem(parent.star);
  371.         }
  372.         parentSystem = solarSystem->getPlanets();
  373.     }
  374.     else if (parent.body != NULL)
  375.     {
  376.         // Parent is a planet or moon
  377.         parentSystem = parent.body->getSatellites();
  378.         if (parentSystem == NULL)
  379.         {
  380.             // If the planet doesn't already have any satellites, we
  381.             // have to create a new planetary system for it.
  382.             parentSystem = new PlanetarySystem(parent.body);
  383.             parent.body->setSatellites(parentSystem);
  384.         }
  385.         orbitsPlanet = true;
  386.     }
  387.     else
  388.     {
  389.         cout << "Parent body '" << parentName << "' of '" << name << "' not found.n";
  390.         return false;
  391.     }
  392.     if (parentSystem != NULL)
  393.     {
  394.         ctx->body = new Body(parentSystem);
  395.         ctx->body->setName(reinterpret_cast<const char*>(name));
  396.         parentSystem->addBody(ctx->body);
  397.         return true;
  398.     }
  399.     return false;
  400. }
  401. static ResourceHandle createTexture(ParserContext* ctx, const xmlChar** att)
  402. {
  403.     const xmlChar* type = reinterpret_cast<const xmlChar*>("base");
  404.     const xmlChar* image = NULL;
  405.     bool compress = false;
  406.     // Get the type, image, and compress attributes
  407.     if (att != NULL)
  408.     {
  409.         for (int i = 0; att[i] != NULL; i += 2)
  410.         {
  411.             if (matchName(att[i], "type"))
  412.                 type = att[i + 1];
  413.             else if (matchName(att[i], "image"))
  414.                 image = att[i + 1];
  415.             else if (matchName(att[i], "compress"))
  416.                 parseBoolean(att[i + 1], compress);
  417.         }
  418.     }
  419.     if (image == NULL)
  420.     {
  421.         cout << "Texture has no image source.n";
  422.         return false;
  423.     }
  424.     ResourceHandle texHandle = GetTextureManager()->getHandle(TextureInfo(reinterpret_cast<const char*>(image), compress));
  425.     if (ctx->state == SurfaceState)
  426.     {
  427.         assert(ctx->body != NULL);
  428.         if (matchName(type, "base"))
  429.             ctx->body->getSurface().baseTexture = texHandle;
  430.         else if (matchName(type, "night"))
  431.             ctx->body->getSurface().nightTexture = texHandle;
  432.     }
  433.     else if (ctx->state == AtmosphereState)
  434.     {
  435.         assert(ctx->body != NULL);
  436.         Atmosphere* atmosphere = ctx->body->getAtmosphere();
  437.         assert(atmosphere != NULL);
  438.         if (matchName(type, "base"))
  439.             atmosphere->cloudTexture = texHandle;
  440.     }
  441.     else if (ctx->state == RingsState)
  442.     {
  443.         assert(ctx->body != NULL);
  444.         assert(ctx->body->getRings() != NULL);
  445.         if (matchName(type, "base"))
  446.             ctx->body->getRings()->texture = texHandle;
  447.     }
  448.     return true;
  449. }
  450. static ResourceHandle createBumpMap(ParserContext* ctx, const xmlChar** att)
  451. {
  452.     const xmlChar* heightmap = NULL;
  453.     float bumpHeight = 2.5f;
  454.     // Get the type, image, and compress attributes
  455.     if (att != NULL)
  456.     {
  457.         for (int i = 0; att[i] != NULL; i += 2)
  458.         {
  459.             if (matchName(att[i], "heightmap"))
  460.                 heightmap = att[i + 1];
  461.             else if (matchName(att[i], "bump-height"))
  462.                 parseNumber(att[i + 1], bumpHeight);
  463.         }
  464.     }
  465.     if (heightmap == NULL)
  466.     {
  467.         cout << "Bump map has no height map source.n";
  468.         return false;
  469.     }
  470.     ResourceHandle texHandle = GetTextureManager()->getHandle(TextureInfo(reinterpret_cast<const char*>(heightmap), bumpHeight));
  471.     if (ctx->state == SurfaceState)
  472.     {
  473.         assert(ctx->body != NULL);
  474.         if (texHandle != InvalidResource)
  475.         {
  476.             ctx->body->getSurface().bumpTexture = texHandle;
  477.             ctx->body->getSurface().appearanceFlags |= Surface::ApplyBumpMap;
  478.         }
  479.         return true;
  480.     }
  481.     else
  482.     {
  483.         return false;
  484.     }
  485. }
  486. static bool createAtmosphere(ParserContext* ctx, const xmlChar** att)
  487. {
  488.     Atmosphere* atmosphere = new Atmosphere();
  489.     if (att != NULL)
  490.     {
  491.         for (int i = 0; att[i] != NULL; i += 2)
  492.         {
  493.             if (matchName(att[i], "height"))
  494.                 parseDistance(att[i + 1], atmosphere->height, "km");
  495.             else if (matchName(att[i], "lower-color"))
  496.                 parseColor(att[i + 1], atmosphere->lowerColor);
  497.             else if (matchName(att[i], "upper-color"))
  498.                 parseColor(att[i + 1], atmosphere->upperColor);
  499.             else if (matchName(att[i], "sky-color"))
  500.                 parseColor(att[i + 1], atmosphere->skyColor);
  501.             else if (matchName(att[i], "cloud-height"))
  502.                 parseDistance(att[i + 1], atmosphere->cloudHeight, "km");
  503.             else if (matchName(att[i], "cloud-speed"))
  504.                 parseAngle(att[i + 1], atmosphere->cloudSpeed);
  505.         }
  506.     }
  507.     assert(ctx->body != NULL);
  508.     ctx->body->setAtmosphere(*atmosphere);
  509.     delete atmosphere;
  510.     
  511.     return true;
  512. }
  513. static bool createHaze(ParserContext* ctx, const xmlChar** att)
  514. {
  515.     Color hazeColor;
  516.     float hazeDensity = 0.0f;
  517.     if (att != NULL)
  518.     {
  519.         for (int i = 0; att[i] != NULL; i += 2)
  520.         {
  521.             if (matchName(att[i], "density"))
  522.                 parseNumber(att[i + 1], hazeDensity);
  523.             else if (matchName(att[i], "color"))
  524.                 parseColor(att[i + 1], hazeColor);
  525.         }
  526.     }
  527.     assert(ctx->body != NULL);
  528.     ctx->body->getSurface().hazeColor = Color(hazeColor.red(),
  529.                                               hazeColor.green(),
  530.                                               hazeColor.blue(),
  531.                                               hazeDensity);
  532.     
  533.     return true;
  534. }
  535. static bool createSurface(ParserContext* ctx, const xmlChar** att)
  536. {
  537.     Color color(1.0f, 1.0f, 1.0f);
  538.     Color specularColor(0.0f, 0.0f, 0.0f);
  539.     float specularPower = 0.0f;
  540.     float albedo = 0.5f;
  541.     bool blendTexture = false;
  542.     bool emissive = false;
  543.     if (att != NULL)
  544.     {
  545.         for (int i = 0; att[i] != NULL; i += 2)
  546.         {
  547.             if (matchName(att[i], "color"))
  548.                 parseColor(att[i + 1], color);
  549.             if (matchName(att[i], "specular-color"))
  550.                 parseColor(att[i + 1], specularColor);
  551.             if (matchName(att[i], "specular-power"))
  552.                 parseNumber(att[i + 1], specularPower);
  553.             if (matchName(att[i], "blend-texture"))
  554.                 parseBoolean(att[i + 1], blendTexture);
  555.             if (matchName(att[i], "emissive"))
  556.                 parseBoolean(att[i + 1], emissive);
  557.             if (matchName(att[i], "albedo"))
  558.                 parseNumber(att[i + 1], albedo);
  559.         }
  560.     }
  561.     assert(ctx->body != NULL);
  562.     ctx->body->setAlbedo(albedo);
  563.     ctx->body->getSurface().color = color;
  564.     ctx->body->getSurface().specularColor = specularColor;
  565.     ctx->body->getSurface().specularPower = specularPower;
  566.     if (blendTexture)
  567.         ctx->body->getSurface().appearanceFlags |= Surface::BlendTexture;
  568.     if (emissive)
  569.         ctx->body->getSurface().appearanceFlags |= Surface::Emissive;
  570.     return true;
  571. }
  572. static bool createEllipticalOrbit(ParserContext* ctx, const xmlChar** att)
  573. {
  574.     // SemiMajorAxis and Period are absolutely required; everything
  575.     // else has a reasonable default.
  576.     double pericenterDistance = 0.0;
  577.     double semiMajorAxis = 0.0;
  578.     double period = 0.0;
  579.     double eccentricity = 0.0;
  580.     double inclination = 0.0;
  581.     double ascendingNode = 0.0;
  582.     double argOfPericenter = 0.0;
  583.     double anomalyAtEpoch = 0.0;
  584.     double epoch = astro::J2000;
  585.     bool foundPeriod = false;
  586.     bool foundSMA = false;
  587.     bool foundPD = false;
  588.     // On the first pass through the attribute list, extract the
  589.     // period, epoch, ascending node, semi-major axis, eccentricity,
  590.     // and inclination.
  591.     if (att != NULL)
  592.     {
  593.         int i;
  594.         for (i = 0; att[i] != NULL; i += 2)
  595.         {
  596.             if (matchName(att[i], "period"))
  597.             {
  598.                 foundPeriod = true;
  599.                 parseTime(att[i + 1], period, "d");
  600.             }
  601.             else if (matchName(att[i], "semi-major-axis"))
  602.             {
  603.                 foundSMA = true;
  604.                 parseDistance(att[i + 1], semiMajorAxis, "km");
  605.                 cout << "SMA: " << semiMajorAxis << 'n';
  606.             }
  607.             else if (matchName(att[i], "pericenter-distance"))
  608.             {
  609.                 foundPD = true;
  610.                 parseDistance(att[i + 1], pericenterDistance, "km");
  611.             }
  612.             else if (matchName(att[i], "epoch"))
  613.                 parseEpoch(att[i + 1], epoch);
  614.             else if (matchName(att[i], "eccentricity"))
  615.                 parseNumber(att[i + 1], eccentricity);
  616.             else if (matchName(att[i], "inclination"))
  617.                 parseAngle(att[i + 1], inclination);
  618.             else if (matchName(att[i], "ascending-node"))
  619.                 parseAngle(att[i + 1], ascendingNode);
  620.         }
  621.         // On the next pass, get the argument or longitude of pericenter; it's
  622.         // important that we get the longitude of pericenter after we know the
  623.         // ascending node, because this value is required to convert to
  624.         // argument of pericenter
  625.         for (i = 0; att[i] != NULL; i += 2)
  626.         {
  627.             if (matchName(att[i], "arg-of-pericenter"))
  628.             {
  629.                 parseAngle(att[i + 1], argOfPericenter);
  630.             }
  631.             else if (matchName(att[i + 1], "long-of-pericenter"))
  632.             {
  633.                 double longOfPericenter;
  634.                 parseAngle(att[i + 1], longOfPericenter);
  635.                 argOfPericenter = longOfPericenter - ascendingNode;
  636.             }
  637.         }
  638.         // On the third pass, get the anomaly or mean longitude; converting
  639.         // from mean longitude to anomaly requires the arg of pericenter from the
  640.         // second pass.
  641.         for (i = 0; att[i] != NULL; i += 2)
  642.         {
  643.             if (matchName(att[i], "mean-anomaly"))
  644.             {
  645.                 parseAngle(att[i + 1], anomalyAtEpoch);
  646.             }
  647.             else if (matchName(att[i + 1], "mean-longitude"))
  648.             {
  649.                 double longAtEpoch;
  650.                 parseAngle(att[i + 1], longAtEpoch);
  651.                 anomalyAtEpoch = longAtEpoch - (argOfPericenter + ascendingNode);
  652.             }
  653.         }
  654.     }
  655.     if (!foundPeriod)
  656.     {
  657.         return false;
  658.     }
  659.     else if (!foundSMA && !foundPD)
  660.     {
  661.         return false;
  662.     }
  663.     // If we read the semi-major axis, use it to compute the pericenter
  664.     // distance.
  665.     if (foundSMA)
  666.         pericenterDistance = semiMajorAxis * (1.0 - eccentricity);
  667.     EllipticalOrbit* orbit = new EllipticalOrbit(pericenterDistance,
  668.                                                  eccentricity,
  669.                                                  degToRad(inclination),
  670.                                                  degToRad(ascendingNode),
  671.                                                  degToRad(argOfPericenter),
  672.                                                  degToRad(anomalyAtEpoch),
  673.                                                  period,
  674.                                                  epoch);
  675.     assert(ctx->body != NULL);
  676.     // Custom orbits have precedence over elliptical orbits, so don't set
  677.     // the orbit if the object already has one assigned.
  678.     if (ctx->body->getOrbit() == NULL)
  679.         ctx->body->setOrbit(orbit);
  680.     else
  681.         delete orbit;
  682.     return true;
  683. }
  684. static bool createCustomOrbit(ParserContext* ctx, const xmlChar** att)
  685. {
  686.     const xmlChar* name = NULL;
  687.     // Get the type, image, and compress attributes
  688.     if (att != NULL)
  689.     {
  690.         for (int i = 0; att[i] != NULL; i += 2)
  691.         {
  692.             if (matchName(att[i], "name"))
  693.                 name = att[i + 1];
  694.         }
  695.     }
  696.     if (name == NULL)
  697.         return false;
  698.     Orbit* orbit = GetCustomOrbit(reinterpret_cast<const char*>(name));
  699.     if (orbit == NULL)
  700.     {
  701.         DPRINTF(0, "Could not find custom orbit named '%s'n",
  702.                 reinterpret_cast<const char*>(name));
  703.     }
  704.     else
  705.     {
  706.         assert(ctx->body != NULL);
  707.         ctx->body->setOrbit(orbit);
  708.     }
  709.     return true;
  710. }
  711. static bool createRotation(ParserContext* ctx, const xmlChar** att)
  712. {
  713.     double period = 0.0;
  714.     double obliquity = 0.0;
  715.     double axisLongitude = 0.0;
  716.     double offset = 0.0;
  717.     double epoch = astro::J2000;
  718.     if (att != NULL)
  719.     {
  720.         for (int i = 0; att[i] != NULL; i += 2)
  721.         {
  722.             if (matchName(att[i], "period"))
  723.             {
  724.                 if (matchName(att[i + 1], "sync"))
  725.                     period = 0.0;
  726.                 else
  727.                     parseTime(att[i + 1], period, "h");
  728.             }
  729.             else if (matchName(att[i], "obliquity"))
  730.                 parseAngle(att[i + 1], obliquity);
  731.             else if (matchName(att[i], "axis-longitude"))
  732.                 parseAngle(att[i + 1], axisLongitude);
  733.             else if (matchName(att[i], "offset"))
  734.                 parseAngle(att[i + 1], offset);
  735.             else if (matchName(att[i], "epoch"))
  736.                 parseEpoch(att[i + 1], epoch);
  737.         }
  738.     }
  739.     assert(ctx->body != NULL);
  740.     RotationElements re;
  741.     // A period of 0 means that the object is in synchronous rotation, so
  742.     // we'll set its rotation period equal to its orbital period.  The catch
  743.     // is that we require that the orbit was specified before the rotation
  744.     // elements within the XML file.
  745.     Orbit* orbit = ctx->body->getOrbit();
  746.     if (orbit == NULL)
  747.         return false;
  748.     if (period == 0.0)
  749.         re.period = (float) orbit->getPeriod();
  750.     else
  751.         re.period = (float) period / 24.0f;
  752.     re.obliquity = (float) degToRad(obliquity);
  753.     re.axisLongitude = (float) degToRad(axisLongitude);
  754.     re.offset = (float) degToRad(offset);
  755.     re.epoch = epoch;
  756.     ctx->body->setRotationElements(re);
  757.     
  758.     return true;
  759. }
  760. static bool createGeometry(ParserContext* ctx, const xmlChar** att)
  761. {
  762.     double radius = 1.0;
  763.     double oblateness = 0.0;
  764.     const xmlChar* meshName = NULL;
  765.     // Get the radius and mesh attributes
  766.     if (att != NULL)
  767.     {
  768.         for (int i = 0; att[i] != NULL; i += 2)
  769.         {
  770.             if (matchName(att[i], "radius"))
  771.                 parseDistance(att[i + 1], radius, "km");
  772.             else if (matchName(att[i], "mesh"))
  773.                 meshName = att[i + 1];
  774.             else if (matchName(att[i], "oblateness"))
  775.                 parseNumber(att[i + 1], oblateness);
  776.         }
  777.     }
  778.     assert(ctx->body != NULL);
  779.     ResourceHandle meshHandle = InvalidResource;
  780.     if (meshName != NULL)
  781.         meshHandle = GetMeshManager()->getHandle(MeshInfo(reinterpret_cast<const char*>(meshName)));
  782.     ctx->body->setMesh(meshHandle);
  783.     ctx->body->setRadius((float) radius);
  784.     ctx->body->setOblateness((float) oblateness);
  785.     return false;
  786. }
  787. static bool createRings(ParserContext* ctx, const xmlChar** att)
  788. {
  789.     double innerRadius = 0.0;
  790.     double outerRadius = 0.0;
  791.     Color color(1.0f, 1.0f, 1.0f);
  792.     // Get the radius and color attributes
  793.     if (att != NULL)
  794.     {
  795.         for (int i = 0; att[i] != NULL; i += 2)
  796.         {
  797.             if (matchName(att[i], "inner-radius"))
  798.                 parseDistance(att[i + 1], innerRadius, "km");
  799.             else if (matchName(att[i], "outer-radius"))
  800.                 parseDistance(att[i + 1], outerRadius, "km");
  801.             else if (matchName(att[i], "color"))
  802.                 parseColor(att[i + 1], color);
  803.         }
  804.     }
  805.     assert(ctx->body != NULL);
  806.     ctx->body->setRings(RingSystem(innerRadius, outerRadius, color));
  807.     return true;
  808. }
  809. // SAX startDocument callback
  810. static void solarSysStartDocument(void* data)
  811. {
  812.     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
  813.     ctx->state = StartState;
  814.     ctx->body = NULL;
  815. }
  816. // SAX endDocument callback
  817. static void solarSysEndDocument(void* data)
  818. {
  819.     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
  820.     ctx->state = EndState;
  821.     ctx->body = NULL;
  822. }
  823. // SAX startElement callback
  824. static void solarSysStartElement(void* data,
  825.                                  const xmlChar* name,
  826.                                  const xmlChar** att)
  827. {
  828.     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
  829.     switch (ctx->state)
  830.     {
  831.     case ErrorState:
  832.         return;
  833.     case StartState:
  834.         if (matchName(name, "body"))
  835.         {
  836.             createBody(ctx, att);
  837.             ctx->state = BodyState;
  838.         }
  839.         else if (!matchName(name, "catalog"))
  840.         {
  841.             ctx->state = ErrorState;
  842.         }
  843.         break;
  844.     case BodyState:
  845.         if (matchName(name, "surface"))
  846.         {
  847.             createSurface(ctx, att);
  848.             ctx->state = SurfaceState;
  849.         }
  850.         else if (matchName(name, "geometry"))
  851.         {
  852.             createGeometry(ctx, att);
  853.             ctx->state = BodyLeafState;
  854.         }
  855.         else if (matchName(name, "elliptical"))
  856.         {
  857.             createEllipticalOrbit(ctx, att);
  858.             ctx->state = BodyLeafState;
  859.         }
  860.         else if (matchName(name, "customorbit"))
  861.         {
  862.             createCustomOrbit(ctx, att);
  863.             ctx->state = BodyLeafState;
  864.         }
  865.         else if (matchName(name, "rotation"))
  866.         {
  867.             createRotation(ctx, att);
  868.             ctx->state = BodyLeafState;
  869.         }
  870.         else if (matchName(name, "atmosphere"))
  871.         {
  872.             createAtmosphere(ctx, att);
  873.             ctx->state = AtmosphereState;
  874.         }
  875.         else if (matchName(name, "rings"))
  876.         {
  877.             createRings(ctx, att);
  878.             ctx->state = RingsState;
  879.         }
  880.         else
  881.         {
  882.             ctx->state = ErrorState;
  883.         }
  884.         break;
  885.     case SurfaceState:
  886.         if (matchName(name, "texture"))
  887.         {
  888.             createTexture(ctx, att);
  889.             ctx->state = SurfaceLeafState;
  890.         }
  891.         else if (matchName(name, "bumpmap"))
  892.         {
  893.             createBumpMap(ctx, att);
  894.             ctx->state = SurfaceLeafState;
  895.         }
  896.         else if (matchName(name, "haze"))
  897.         {
  898.             createHaze(ctx, att);
  899.             ctx->state = SurfaceLeafState;
  900.         }
  901.         else
  902.         {
  903.             ctx->state = ErrorState;
  904.         }
  905.         break;
  906.     case RingsState:
  907.         if (matchName(name, "texture"))
  908.         {
  909.             createTexture(ctx, att);
  910.             ctx->state = RingsLeafState;
  911.         }
  912.         break;
  913.     case AtmosphereState:
  914.         if (matchName(name, "texture"))
  915.         {
  916.             createTexture(ctx, att);
  917.             ctx->state = AtmosphereLeafState;
  918.         }
  919.         break;
  920.     case BodyLeafState:
  921.     case SurfaceLeafState:
  922.     case AtmosphereLeafState:
  923.     case RingsLeafState:
  924.         ctx->state = ErrorState;
  925.         break;
  926.     default:
  927.         break;
  928.     }
  929.     if (ctx->state == ErrorState)
  930.     {
  931.         cout << "Error!  " << name << " element not expected.n";
  932.     }
  933. }
  934. // SAX endElement callback
  935. static void solarSysEndElement(void* data, const xmlChar* name)
  936. {
  937.     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
  938.     switch (ctx->state)
  939.     {
  940.     case ErrorState:
  941.         return;
  942.     case BodyState:
  943.         if (matchName(name, "body"))
  944.         {
  945.             assert(ctx->body != NULL);
  946.             if (ctx->body->getOrbit() == NULL)
  947.             {
  948.                 DPRINTF(0, "Object %s has no orbit!  Removing . . .n",
  949.                         ctx->body->getName().c_str());
  950.                 
  951.             }
  952.             ctx->body = NULL;
  953.             ctx->state = StartState;
  954.         }
  955.         else
  956.         {
  957.             ctx->state = ErrorState;
  958.         }
  959.         break;
  960.     case SurfaceState:
  961.         if (matchName(name, "surface"))
  962.             ctx->state = BodyState;
  963.         else
  964.             ctx->state = ErrorState;
  965.         break;
  966.     case AtmosphereState:
  967.         if (matchName(name, "atmosphere"))
  968.             ctx->state = BodyState;
  969.         else
  970.             ctx->state = ErrorState;
  971.         break;
  972.     case RingsState:
  973.         if (matchName(name, "rings"))
  974.             ctx->state = BodyState;
  975.         else
  976.             ctx->state = ErrorState;
  977.         break;
  978.     case BodyLeafState:
  979.         if (matchName(name, "geometry") ||
  980.             matchName(name, "elliptical") ||
  981.             matchName(name, "customorbit") ||
  982.             matchName(name, "rotation"))
  983.         {
  984.             ctx->state = BodyState;
  985.         }
  986.         break;
  987.     case SurfaceLeafState:
  988.         if (matchName(name, "texture") ||
  989.             matchName(name, "haze") ||
  990.             matchName(name, "bumpmap"))
  991.         {
  992.             ctx->state = SurfaceState;
  993.         }
  994.         break;
  995.     case AtmosphereLeafState:
  996.         if (matchName(name, "texture"))
  997.             ctx->state = AtmosphereState;
  998.         break;
  999.     case RingsLeafState:
  1000.         if (matchName(name, "texture"))
  1001.             ctx->state = RingsState;
  1002.         break;
  1003.     default:
  1004.         break;
  1005.     }
  1006.     if (ctx->state == ErrorState)
  1007.     {
  1008.         cout << "Error!  End of " << name << " element not expected.n";
  1009.     }
  1010. }
  1011. static void initSAXHandler(xmlSAXHandler& sax)
  1012. {
  1013.     sax = emptySAXHandler;
  1014.     sax.startDocument = solarSysStartDocument;
  1015.     sax.endDocument = solarSysEndDocument;
  1016.     sax.startElement = solarSysStartElement;
  1017.     sax.endElement = solarSysEndElement;
  1018. }
  1019. static bool parseSolarSystemXML(xmlSAXHandlerPtr sax,
  1020.                                 void* userData,
  1021.                                 const char* filename)
  1022. {
  1023.     xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename);
  1024.     if (ctxt == NULL)
  1025.         return false;
  1026.     ctxt->sax = sax;
  1027.     ctxt->userData = userData;
  1028.     xmlParseDocument(ctxt);
  1029.     int wellFormed = ctxt->wellFormed;
  1030.     if (sax != NULL)
  1031.         ctxt->sax = NULL;
  1032.     xmlFreeParserCtxt(ctxt);
  1033.     cout << "Well formed: " << wellFormed << 'n';
  1034.     return wellFormed != 0;
  1035. }
  1036. bool LoadSolarSystemObjectsXML(const string& source, Universe& universe)
  1037. {
  1038.     initSAXHandler(saxHandler);
  1039.     ParserContext ctx;
  1040.     ctx.universe = &universe;
  1041.     if (!parseSolarSystemXML(&saxHandler, &ctx, source.c_str()))
  1042.     {
  1043.         cout << "Error parsing " << source << 'n';
  1044.         return false;
  1045.     }
  1046.     return true;
  1047. }