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

solarsysxml.cpp

Go to the documentation of this file.
00001 // solarsysxml.cpp
00002 //
00003 // Copyright (C) 2001 Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <cassert>
00011 
00012 #include <libxml/parser.h>
00013 #include <libxml/parserInternals.h>
00014 #include <libxml/SAX.h>
00015 
00016 #include <celutil/debug.h>
00017 #include <celmath/mathlib.h>
00018 #include "astro.h"
00019 #include "customorbit.h"
00020 #include "texmanager.h"
00021 #include "meshmanager.h"
00022 #include "solarsysxml.h"
00023 
00024 using namespace std;
00025 
00026 
00027 struct UnitDefinition
00028 {
00029     char* name;
00030     double conversion;
00031 };
00032 
00033 UnitDefinition distanceUnits[] =
00034 {
00035     { "km", 1.0 },
00036     { "m",  0.001 },
00037     { "au", 149597870.7 },
00038     { "ly", 9466411842000.0 },
00039 };
00040 
00041 UnitDefinition timeUnits[] =
00042 {
00043     { "s", 1.0 },
00044     { "m", 60.0 },
00045     { "h", 3600.0 },
00046     { "d", 86400.0 },
00047     { "y", 86400.0 * 365.25 },
00048 };
00049 
00050 
00051 static xmlSAXHandler emptySAXHandler =
00052 {
00053     NULL, /* internalSubset */
00054     NULL, /* isStandalone */
00055     NULL, /* hasInternalSubset */
00056     NULL, /* hasExternalSubset */
00057     NULL, /* resolveEntity */
00058     NULL, /* getEntity */
00059     NULL, /* entityDecl */
00060     NULL, /* notationDecl */
00061     NULL, /* attributeDecl */
00062     NULL, /* elementDecl */
00063     NULL, /* unparsedEntityDecl */
00064     NULL, /* setDocumentLocator */
00065     NULL, /* startDocument */
00066     NULL, /* endDocument */
00067     NULL, /* startElement */
00068     NULL, /* endElement */
00069     NULL, /* reference */
00070     NULL, /* characters */
00071     NULL, /* ignorableWhitespace */
00072     NULL, /* processingInstruction */
00073     NULL, /* comment */
00074     NULL, /* xmlParserWarning */
00075     NULL, /* xmlParserError */
00076     NULL, /* xmlParserError */
00077     NULL, /* getParameterEntity */
00078     NULL, /* cdataBlock; */
00079     NULL,  /* externalSubset; */
00080     1
00081 };
00082 
00083 static xmlSAXHandler saxHandler;
00084 
00085 enum ParserState {
00086     StartState,
00087     EndState,
00088     BodyState,
00089     SurfaceState,
00090     AtmosphereState,
00091     RingsState,
00092     BodyLeafState,
00093     SurfaceLeafState,
00094     AtmosphereLeafState,
00095     RingsLeafState,
00096     ErrorState,
00097 };
00098 
00099 struct ParserContext
00100 {
00101     ParserState state;
00102     Body* body;
00103     Universe* universe;
00104 };
00105 
00106 
00107 static bool matchName(const xmlChar* s, const char* name)
00108 {
00109     while ((char) *s == *name)
00110     {
00111         if (*s == '\0')
00112             return true;
00113         s++;
00114         name++;
00115     }
00116 
00117     return false;
00118 }
00119 
00120 
00121 static bool parseBoolean(const xmlChar* s, bool& b)
00122 {
00123     if (matchName(s, "true") || matchName(s, "1") || matchName(s, "on"))
00124     {
00125         b = true;
00126         return true;
00127     }
00128     else if (matchName(s, "false") || matchName(s, "0") || matchName(s, "off"))
00129     {
00130         b = false;
00131         return true;
00132     }
00133     else
00134     {
00135         return false;
00136     }
00137 }
00138 
00139 
00140 static bool parseNumber(const xmlChar* s, double& d)
00141 {
00142     return sscanf((char*) s, "%lf", &d) == 1;
00143 }
00144 
00145 
00146 static bool parseNumber(const xmlChar* s, float& f)
00147 {
00148     double d;
00149     if (parseNumber(s, d))
00150     {
00151         f = (float) d;
00152         return true;
00153     }
00154     else
00155     {
00156         return false;
00157     }
00158 }
00159 
00160 
00161 static bool parseNumberUnits(const xmlChar* s,
00162                              double& d,
00163                              UnitDefinition* unitTable,
00164                              int unitTableLength,
00165                              char* defaultUnitName)
00166 {
00167     char unitName[4];
00168     double value;
00169     int nMatched = sscanf(reinterpret_cast<const char*>(s),
00170                           "%lf%3s", &value, unitName);
00171     cout << "parseNumberUnits(" << reinterpret_cast<const char*>(s) << ")\n";
00172     if (nMatched == 1)
00173     {
00174         cout << "matched = 1: " << reinterpret_cast<const char*>(s) << '\n';
00175         d = value;
00176         return true;
00177     }
00178     else if (nMatched == 2)
00179     {
00180         cout << "matched = 2: " << reinterpret_cast<const char*>(s) << '\n';
00181         // Found a value and units; multiply the value by a conversion
00182         // factor to produce default units
00183         UnitDefinition* defaultUnit = NULL;
00184         UnitDefinition* unit = NULL;
00185         int i;
00186 
00187         // Get the default unit
00188         for (i = 0; i < unitTableLength; i++)
00189         {
00190             if (strcmp(defaultUnitName, unitTable[i].name) == 0)
00191             {
00192                 defaultUnit = &unitTable[i];
00193                 break;
00194             }
00195         }
00196         assert(defaultUnit != NULL);
00197 
00198         // Try and the match the unit name specified in the xml attribute
00199         for (i = 0; i < unitTableLength; i++)
00200         {
00201             if (strcmp(unitName, unitTable[i].name) == 0)
00202             {
00203                 unit = &unitTable[i];
00204                 break;
00205             }
00206         }
00207 
00208         if (unit != NULL)
00209         {
00210             // Match found; perform the conversion
00211             d = value * unit->conversion  / defaultUnit->conversion;
00212             cout << "converting: " << value << unit->name << " = " << d << defaultUnit->name << "\n";
00213             return true;
00214         }
00215         else
00216         {
00217             return false;
00218         }
00219     }
00220     else
00221     {
00222         // Error . . . we matched nothing.
00223         return false;
00224     }
00225 }
00226 
00227 
00228 static bool parseDistance(const xmlChar* s, double& d, char* defaultUnitName)
00229 {
00230     return parseNumberUnits(s, d,
00231                             distanceUnits,
00232                             sizeof distanceUnits / sizeof distanceUnits[0],
00233                             defaultUnitName);
00234 }
00235 
00236 
00237 static bool parseDistance(const xmlChar* s, float& f, char* defaultUnitName)
00238 {
00239     double d;
00240     if (parseDistance(s, d, defaultUnitName))
00241     {
00242         f = (float) d;
00243         return true;
00244     }
00245     else
00246     {
00247         return false;
00248     }
00249 }
00250 
00251 
00252 static bool parseAngle(const xmlChar* s, double& d)
00253 {
00254     return parseNumber(s, d);
00255 }
00256 
00257 
00258 static bool parseAngle(const xmlChar* s, float& f)
00259 {
00260     double d;
00261     if (parseAngle(s, d))
00262     {
00263         f = (float) d;
00264         return true;
00265     }
00266     else
00267     {
00268         return false;
00269     }
00270 }
00271 
00272 
00273 static bool parseTime(const xmlChar* s, double& d, char* defaultUnitName)
00274 {
00275     return parseNumberUnits(s, d,
00276                             timeUnits,
00277                             sizeof timeUnits / sizeof timeUnits[0],
00278                             defaultUnitName);
00279 }
00280 
00281 
00282 static bool parseTime(const xmlChar* s, float& f, char* defaultUnitName)
00283 {
00284     double d;
00285     if (parseTime(s, d, defaultUnitName))
00286     {
00287         f = (float) d;
00288         return true;
00289     }
00290     else
00291     {
00292         return false;
00293     }
00294 }
00295 
00296 
00297 static bool parseEpoch(const xmlChar* s, double& d)
00298 {
00299     if (matchName(s, "J2000"))
00300     {
00301         d = astro::J2000;
00302         return true;
00303     }
00304     else
00305     {
00306         return parseNumber(s, d);
00307     }
00308 }
00309 
00310 
00311 static int hexDigit(char c)
00312 {
00313     // Assumes an ASCII character set . . .
00314     if (c >= '0' && c <= '9')
00315         return c - '0';
00316     else if (c >= 'a' && c <= 'f')
00317         return 10 + (c - 'a');
00318     else if (c >= 'A' && c <= 'F')
00319         return 10 + (c - 'A');
00320     else
00321         return 0; // bad digit
00322 }
00323 
00324 
00325 ostream& operator<<(ostream& out, const Color& c)
00326 {
00327     cout << '[' << c.red() << ',' << c.green() << ',' << c.blue() << ']';
00328     return cout;
00329 }
00330 
00331 
00332 static bool parseColor(const xmlChar* s, Color& c)
00333 {
00334     const char* colorName = reinterpret_cast<const char*>(s);
00335     char hexColor[7];
00336     float r = 0.0f;
00337     float g = 0.0f;
00338     float b = 0.0f;
00339 
00340     cout << "parsing color: " << colorName << '\n';
00341 
00342     if (sscanf(colorName, " #%6[0-9a-fA-F] ", hexColor))
00343     {
00344         if (strlen(hexColor) == 6)
00345         {
00346             c = Color((hexDigit(hexColor[0]) * 16 +
00347                        hexDigit(hexColor[1])) / 255.0f,
00348                       (hexDigit(hexColor[2]) * 16 +
00349                        hexDigit(hexColor[3])) / 255.0f,
00350                       (hexDigit(hexColor[4]) * 16 +
00351                        hexDigit(hexColor[5])) / 255.0f);
00352             cout << "1: " << c << '\n';
00353             return true;
00354         }
00355         else if (strlen(hexColor) == 3)
00356         {
00357             c = Color((hexDigit(hexColor[0]) * 17) / 255.0f,
00358                       (hexDigit(hexColor[1]) * 17) / 255.0f,
00359                       (hexDigit(hexColor[2]) * 17) / 255.0f);
00360             cout << "2: " << c << '\n';
00361             return true;
00362         }
00363         else
00364         {
00365             return false;
00366         }
00367     }
00368     else if (sscanf(colorName, " rgb( %f , %f , %f ) ", &r, &g, &b) == 3)
00369     {
00370         c = Color(r / 255.0f, g / 255.0f, b / 255.0f);
00371         cout << "3: " << c << '\n';
00372         return true;
00373     }
00374     else if (sscanf(colorName, " rgb( %f%% , %f%% , %f%% ) ", &r, &g, &b) == 3)
00375     {
00376         c = Color(r / 100.0f, g / 100.0f, b / 100.0f);
00377         cout << "4: " << c << '\n';
00378         return true;
00379     }
00380     else
00381     {
00382         return false;
00383     }
00384 }
00385 
00386 
00387 static bool createBody(ParserContext* ctx, const xmlChar** att)
00388 {
00389     const xmlChar* name = NULL;
00390     const xmlChar* parentName = NULL;
00391 
00392     // Get the name and parent attributes
00393     if (att != NULL)
00394     {
00395         for (int i = 0; att[i] != NULL; i += 2)
00396         {
00397             if (matchName(att[i], "name"))
00398                 name = att[i + 1];
00399             else if (matchName(att[i], "parent"))
00400                 parentName = att[i + 1];
00401         }
00402     }
00403 
00404     // Require that both are present
00405     if (name == NULL)
00406     {
00407         return false;
00408     }
00409     else if (parentName == NULL)
00410     {
00411         return false;
00412     }
00413 
00414     bool orbitsPlanet = false;
00415     Selection parent = ctx->universe->findPath(reinterpret_cast<const char*>(parentName), NULL, 0);
00416     PlanetarySystem* parentSystem = NULL;
00417     if (parent.star != NULL)
00418     {
00419         SolarSystem* solarSystem = ctx->universe->getSolarSystem(parent.star);
00420         if (solarSystem == NULL)
00421         {
00422             // No solar system defined for this star yet, so we need
00423             // to create it.
00424             solarSystem = ctx->universe->createSolarSystem(parent.star);
00425         }
00426         parentSystem = solarSystem->getPlanets();
00427     }
00428     else if (parent.body != NULL)
00429     {
00430         // Parent is a planet or moon
00431         parentSystem = parent.body->getSatellites();
00432         if (parentSystem == NULL)
00433         {
00434             // If the planet doesn't already have any satellites, we
00435             // have to create a new planetary system for it.
00436             parentSystem = new PlanetarySystem(parent.body);
00437             parent.body->setSatellites(parentSystem);
00438         }
00439         orbitsPlanet = true;
00440     }
00441     else
00442     {
00443         cout << "Parent body '" << parentName << "' of '" << name << "' not found.\n";
00444         return false;
00445     }
00446 
00447     if (parentSystem != NULL)
00448     {
00449         ctx->body = new Body(parentSystem);
00450         ctx->body->setName(reinterpret_cast<const char*>(name));
00451         parentSystem->addBody(ctx->body);
00452         return true;
00453     }
00454 
00455     return false;
00456 }
00457 
00458 
00459 static ResourceHandle createTexture(ParserContext* ctx, const xmlChar** att)
00460 {
00461     const xmlChar* type = reinterpret_cast<const xmlChar*>("base");
00462     const xmlChar* image = NULL;
00463     bool compress = false;
00464 
00465     // Get the type, image, and compress attributes
00466     if (att != NULL)
00467     {
00468         for (int i = 0; att[i] != NULL; i += 2)
00469         {
00470             if (matchName(att[i], "type"))
00471                 type = att[i + 1];
00472             else if (matchName(att[i], "image"))
00473                 image = att[i + 1];
00474             else if (matchName(att[i], "compress"))
00475                 parseBoolean(att[i + 1], compress);
00476         }
00477     }
00478 
00479     if (image == NULL)
00480     {
00481         cout << "Texture has no image source.\n";
00482         return false;
00483     }
00484 
00485     ResourceHandle texHandle = GetTextureManager()->getHandle(TextureInfo(reinterpret_cast<const char*>(image), compress));
00486     if (ctx->state == SurfaceState)
00487     {
00488         assert(ctx->body != NULL);
00489 
00490         if (matchName(type, "base"))
00491             ctx->body->getSurface().baseTexture = texHandle;
00492         else if (matchName(type, "night"))
00493             ctx->body->getSurface().nightTexture = texHandle;
00494     }
00495     else if (ctx->state == AtmosphereState)
00496     {
00497         assert(ctx->body != NULL);
00498         Atmosphere* atmosphere = ctx->body->getAtmosphere();
00499         assert(atmosphere != NULL);
00500 
00501         if (matchName(type, "base"))
00502             atmosphere->cloudTexture = texHandle;
00503     }
00504     else if (ctx->state == RingsState)
00505     {
00506         assert(ctx->body != NULL);
00507         assert(ctx->body->getRings() != NULL);
00508 
00509         if (matchName(type, "base"))
00510             ctx->body->getRings()->texture = texHandle;
00511     }
00512 
00513     return true;
00514 }
00515 
00516 
00517 static ResourceHandle createBumpMap(ParserContext* ctx, const xmlChar** att)
00518 {
00519     const xmlChar* heightmap = NULL;
00520     float bumpHeight = 2.5f;
00521 
00522     // Get the type, image, and compress attributes
00523     if (att != NULL)
00524     {
00525         for (int i = 0; att[i] != NULL; i += 2)
00526         {
00527             if (matchName(att[i], "heightmap"))
00528                 heightmap = att[i + 1];
00529             else if (matchName(att[i], "bump-height"))
00530                 parseNumber(att[i + 1], bumpHeight);
00531         }
00532     }
00533 
00534     if (heightmap == NULL)
00535     {
00536         cout << "Bump map has no height map source.\n";
00537         return false;
00538     }
00539 
00540     ResourceHandle texHandle = GetTextureManager()->getHandle(TextureInfo(reinterpret_cast<const char*>(heightmap), bumpHeight));
00541     if (ctx->state == SurfaceState)
00542     {
00543         assert(ctx->body != NULL);
00544         if (texHandle != InvalidResource)
00545         {
00546             ctx->body->getSurface().bumpTexture = texHandle;
00547             ctx->body->getSurface().appearanceFlags |= Surface::ApplyBumpMap;
00548         }
00549         return true;
00550     }
00551     else
00552     {
00553         return false;
00554     }
00555 }
00556 
00557 
00558 static bool createAtmosphere(ParserContext* ctx, const xmlChar** att)
00559 {
00560     Atmosphere* atmosphere = new Atmosphere();
00561 
00562     if (att != NULL)
00563     {
00564         for (int i = 0; att[i] != NULL; i += 2)
00565         {
00566             if (matchName(att[i], "height"))
00567                 parseDistance(att[i + 1], atmosphere->height, "km");
00568             else if (matchName(att[i], "lower-color"))
00569                 parseColor(att[i + 1], atmosphere->lowerColor);
00570             else if (matchName(att[i], "upper-color"))
00571                 parseColor(att[i + 1], atmosphere->upperColor);
00572             else if (matchName(att[i], "sky-color"))
00573                 parseColor(att[i + 1], atmosphere->skyColor);
00574             else if (matchName(att[i], "cloud-height"))
00575                 parseDistance(att[i + 1], atmosphere->cloudHeight, "km");
00576             else if (matchName(att[i], "cloud-speed"))
00577                 parseAngle(att[i + 1], atmosphere->cloudSpeed);
00578         }
00579     }
00580 
00581     assert(ctx->body != NULL);
00582     ctx->body->setAtmosphere(*atmosphere);
00583     delete atmosphere;
00584     
00585     return true;
00586 }
00587 
00588 
00589 static bool createHaze(ParserContext* ctx, const xmlChar** att)
00590 {
00591     Color hazeColor;
00592     float hazeDensity = 0.0f;
00593 
00594     if (att != NULL)
00595     {
00596         for (int i = 0; att[i] != NULL; i += 2)
00597         {
00598             if (matchName(att[i], "density"))
00599                 parseNumber(att[i + 1], hazeDensity);
00600             else if (matchName(att[i], "color"))
00601                 parseColor(att[i + 1], hazeColor);
00602         }
00603     }
00604 
00605     assert(ctx->body != NULL);
00606     ctx->body->getSurface().hazeColor = Color(hazeColor.red(),
00607                                               hazeColor.green(),
00608                                               hazeColor.blue(),
00609                                               hazeDensity);
00610     
00611     return true;
00612 }
00613 
00614 
00615 static bool createSurface(ParserContext* ctx, const xmlChar** att)
00616 {
00617     Color color(1.0f, 1.0f, 1.0f);
00618     Color specularColor(0.0f, 0.0f, 0.0f);
00619     float specularPower = 0.0f;
00620     float albedo = 0.5f;
00621     bool blendTexture = false;
00622     bool emissive = false;
00623 
00624     if (att != NULL)
00625     {
00626         for (int i = 0; att[i] != NULL; i += 2)
00627         {
00628             if (matchName(att[i], "color"))
00629                 parseColor(att[i + 1], color);
00630             if (matchName(att[i], "specular-color"))
00631                 parseColor(att[i + 1], specularColor);
00632             if (matchName(att[i], "specular-power"))
00633                 parseNumber(att[i + 1], specularPower);
00634             if (matchName(att[i], "blend-texture"))
00635                 parseBoolean(att[i + 1], blendTexture);
00636             if (matchName(att[i], "emissive"))
00637                 parseBoolean(att[i + 1], emissive);
00638             if (matchName(att[i], "albedo"))
00639                 parseNumber(att[i + 1], albedo);
00640         }
00641     }
00642 
00643     assert(ctx->body != NULL);
00644     ctx->body->setAlbedo(albedo);
00645     ctx->body->getSurface().color = color;
00646     ctx->body->getSurface().specularColor = specularColor;
00647     ctx->body->getSurface().specularPower = specularPower;
00648     if (blendTexture)
00649         ctx->body->getSurface().appearanceFlags |= Surface::BlendTexture;
00650     if (emissive)
00651         ctx->body->getSurface().appearanceFlags |= Surface::Emissive;
00652 
00653     return true;
00654 }
00655 
00656 
00657 static bool createEllipticalOrbit(ParserContext* ctx, const xmlChar** att)
00658 {
00659     // SemiMajorAxis and Period are absolutely required; everything
00660     // else has a reasonable default.
00661     double pericenterDistance = 0.0;
00662     double semiMajorAxis = 0.0;
00663     double period = 0.0;
00664     double eccentricity = 0.0;
00665     double inclination = 0.0;
00666     double ascendingNode = 0.0;
00667     double argOfPericenter = 0.0;
00668     double anomalyAtEpoch = 0.0;
00669     double epoch = astro::J2000;
00670     bool foundPeriod = false;
00671     bool foundSMA = false;
00672     bool foundPD = false;
00673 
00674     // On the first pass through the attribute list, extract the
00675     // period, epoch, ascending node, semi-major axis, eccentricity,
00676     // and inclination.
00677     if (att != NULL)
00678     {
00679         int i;
00680         for (i = 0; att[i] != NULL; i += 2)
00681         {
00682             if (matchName(att[i], "period"))
00683             {
00684                 foundPeriod = true;
00685                 parseTime(att[i + 1], period, "d");
00686             }
00687             else if (matchName(att[i], "semi-major-axis"))
00688             {
00689                 foundSMA = true;
00690                 parseDistance(att[i + 1], semiMajorAxis, "km");
00691                 cout << "SMA: " << semiMajorAxis << '\n';
00692             }
00693             else if (matchName(att[i], "pericenter-distance"))
00694             {
00695                 foundPD = true;
00696                 parseDistance(att[i + 1], pericenterDistance, "km");
00697             }
00698             else if (matchName(att[i], "epoch"))
00699                 parseEpoch(att[i + 1], epoch);
00700             else if (matchName(att[i], "eccentricity"))
00701                 parseNumber(att[i + 1], eccentricity);
00702             else if (matchName(att[i], "inclination"))
00703                 parseAngle(att[i + 1], inclination);
00704             else if (matchName(att[i], "ascending-node"))
00705                 parseAngle(att[i + 1], ascendingNode);
00706         }
00707 
00708         // On the next pass, get the argument or longitude of pericenter; it's
00709         // important that we get the longitude of pericenter after we know the
00710         // ascending node, because this value is required to convert to
00711         // argument of pericenter
00712         for (i = 0; att[i] != NULL; i += 2)
00713         {
00714             if (matchName(att[i], "arg-of-pericenter"))
00715             {
00716                 parseAngle(att[i + 1], argOfPericenter);
00717             }
00718             else if (matchName(att[i + 1], "long-of-pericenter"))
00719             {
00720                 double longOfPericenter;
00721                 parseAngle(att[i + 1], longOfPericenter);
00722                 argOfPericenter = longOfPericenter - ascendingNode;
00723             }
00724         }
00725 
00726         // On the third pass, get the anomaly or mean longitude; converting
00727         // from mean longitude to anomaly requires the arg of pericenter from the
00728         // second pass.
00729         for (i = 0; att[i] != NULL; i += 2)
00730         {
00731             if (matchName(att[i], "mean-anomaly"))
00732             {
00733                 parseAngle(att[i + 1], anomalyAtEpoch);
00734             }
00735             else if (matchName(att[i + 1], "mean-longitude"))
00736             {
00737                 double longAtEpoch;
00738                 parseAngle(att[i + 1], longAtEpoch);
00739                 anomalyAtEpoch = longAtEpoch - (argOfPericenter + ascendingNode);
00740             }
00741         }
00742     }
00743 
00744     if (!foundPeriod)
00745     {
00746         return false;
00747     }
00748     else if (!foundSMA && !foundPD)
00749     {
00750         return false;
00751     }
00752 
00753     // If we read the semi-major axis, use it to compute the pericenter
00754     // distance.
00755     if (foundSMA)
00756         pericenterDistance = semiMajorAxis * (1.0 - eccentricity);
00757 
00758     EllipticalOrbit* orbit = new EllipticalOrbit(pericenterDistance,
00759                                                  eccentricity,
00760                                                  degToRad(inclination),
00761                                                  degToRad(ascendingNode),
00762                                                  degToRad(argOfPericenter),
00763                                                  degToRad(anomalyAtEpoch),
00764                                                  period,
00765                                                  epoch);
00766     assert(ctx->body != NULL);
00767 
00768     // Custom orbits have precedence over elliptical orbits, so don't set
00769     // the orbit if the object already has one assigned.
00770     if (ctx->body->getOrbit() == NULL)
00771         ctx->body->setOrbit(orbit);
00772     else
00773         delete orbit;
00774 
00775     return true;
00776 }
00777 
00778 
00779 static bool createCustomOrbit(ParserContext* ctx, const xmlChar** att)
00780 {
00781     const xmlChar* name = NULL;
00782 
00783     // Get the type, image, and compress attributes
00784     if (att != NULL)
00785     {
00786         for (int i = 0; att[i] != NULL; i += 2)
00787         {
00788             if (matchName(att[i], "name"))
00789                 name = att[i + 1];
00790         }
00791     }
00792 
00793     if (name == NULL)
00794         return false;
00795 
00796     Orbit* orbit = GetCustomOrbit(reinterpret_cast<const char*>(name));
00797     if (orbit == NULL)
00798     {
00799         DPRINTF(0, "Could not find custom orbit named '%s'\n",
00800                 reinterpret_cast<const char*>(name));
00801     }
00802     else
00803     {
00804         assert(ctx->body != NULL);
00805         ctx->body->setOrbit(orbit);
00806     }
00807 
00808     return true;
00809 }
00810 
00811 
00812 static bool createRotation(ParserContext* ctx, const xmlChar** att)
00813 {
00814     double period = 0.0;
00815     double obliquity = 0.0;
00816     double axisLongitude = 0.0;
00817     double offset = 0.0;
00818     double epoch = astro::J2000;
00819 
00820     if (att != NULL)
00821     {
00822         for (int i = 0; att[i] != NULL; i += 2)
00823         {
00824             if (matchName(att[i], "period"))
00825             {
00826                 if (matchName(att[i + 1], "sync"))
00827                     period = 0.0;
00828                 else
00829                     parseTime(att[i + 1], period, "h");
00830             }
00831             else if (matchName(att[i], "obliquity"))
00832                 parseAngle(att[i + 1], obliquity);
00833             else if (matchName(att[i], "axis-longitude"))
00834                 parseAngle(att[i + 1], axisLongitude);
00835             else if (matchName(att[i], "offset"))
00836                 parseAngle(att[i + 1], offset);
00837             else if (matchName(att[i], "epoch"))
00838                 parseEpoch(att[i + 1], epoch);
00839         }
00840     }
00841 
00842     assert(ctx->body != NULL);
00843 
00844     RotationElements re;
00845     // A period of 0 means that the object is in synchronous rotation, so
00846     // we'll set its rotation period equal to its orbital period.  The catch
00847     // is that we require that the orbit was specified before the rotation
00848     // elements within the XML file.
00849     Orbit* orbit = ctx->body->getOrbit();
00850     if (orbit == NULL)
00851         return false;
00852 
00853     if (period == 0.0)
00854         re.period = (float) orbit->getPeriod();
00855     else
00856         re.period = (float) period / 24.0f;
00857     re.obliquity = (float) degToRad(obliquity);
00858     re.axisLongitude = (float) degToRad(axisLongitude);
00859     re.offset = (float) degToRad(offset);
00860     re.epoch = epoch;
00861     ctx->body->setRotationElements(re);
00862     
00863     return true;
00864 }
00865 
00866 
00867 static bool createGeometry(ParserContext* ctx, const xmlChar** att)
00868 {
00869     double radius = 1.0;
00870     double oblateness = 0.0;
00871     const xmlChar* meshName = NULL;
00872 
00873     // Get the radius and mesh attributes
00874     if (att != NULL)
00875     {
00876         for (int i = 0; att[i] != NULL; i += 2)
00877         {
00878             if (matchName(att[i], "radius"))
00879                 parseDistance(att[i + 1], radius, "km");
00880             else if (matchName(att[i], "mesh"))
00881                 meshName = att[i + 1];
00882             else if (matchName(att[i], "oblateness"))
00883                 parseNumber(att[i + 1], oblateness);
00884         }
00885     }
00886 
00887     assert(ctx->body != NULL);
00888 
00889     ResourceHandle meshHandle = InvalidResource;
00890     if (meshName != NULL)
00891         meshHandle = GetMeshManager()->getHandle(MeshInfo(reinterpret_cast<const char*>(meshName)));
00892     ctx->body->setMesh(meshHandle);
00893     ctx->body->setRadius((float) radius);
00894     ctx->body->setOblateness((float) oblateness);
00895 
00896     return false;
00897 }
00898 
00899 
00900 static bool createRings(ParserContext* ctx, const xmlChar** att)
00901 {
00902     double innerRadius = 0.0;
00903     double outerRadius = 0.0;
00904     Color color(1.0f, 1.0f, 1.0f);
00905 
00906     // Get the radius and color attributes
00907     if (att != NULL)
00908     {
00909         for (int i = 0; att[i] != NULL; i += 2)
00910         {
00911             if (matchName(att[i], "inner-radius"))
00912                 parseDistance(att[i + 1], innerRadius, "km");
00913             else if (matchName(att[i], "outer-radius"))
00914                 parseDistance(att[i + 1], outerRadius, "km");
00915             else if (matchName(att[i], "color"))
00916                 parseColor(att[i + 1], color);
00917         }
00918     }
00919 
00920     assert(ctx->body != NULL);
00921     ctx->body->setRings(RingSystem(innerRadius, outerRadius, color));
00922 
00923     return true;
00924 }
00925 
00926 
00927 // SAX startDocument callback
00928 static void solarSysStartDocument(void* data)
00929 {
00930     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
00931     ctx->state = StartState;
00932     ctx->body = NULL;
00933 }
00934 
00935 
00936 // SAX endDocument callback
00937 static void solarSysEndDocument(void* data)
00938 {
00939     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
00940     ctx->state = EndState;
00941     ctx->body = NULL;
00942 }
00943 
00944 
00945 // SAX startElement callback
00946 static void solarSysStartElement(void* data,
00947                                  const xmlChar* name,
00948                                  const xmlChar** att)
00949 {
00950     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
00951 
00952     switch (ctx->state)
00953     {
00954     case ErrorState:
00955         return;
00956 
00957     case StartState:
00958         if (matchName(name, "body"))
00959         {
00960             createBody(ctx, att);
00961             ctx->state = BodyState;
00962         }
00963         else if (!matchName(name, "catalog"))
00964         {
00965             ctx->state = ErrorState;
00966         }
00967         break;
00968 
00969     case BodyState:
00970         if (matchName(name, "surface"))
00971         {
00972             createSurface(ctx, att);
00973             ctx->state = SurfaceState;
00974         }
00975         else if (matchName(name, "geometry"))
00976         {
00977             createGeometry(ctx, att);
00978             ctx->state = BodyLeafState;
00979         }
00980         else if (matchName(name, "elliptical"))
00981         {
00982             createEllipticalOrbit(ctx, att);
00983             ctx->state = BodyLeafState;
00984         }
00985         else if (matchName(name, "customorbit"))
00986         {
00987             createCustomOrbit(ctx, att);
00988             ctx->state = BodyLeafState;
00989         }
00990         else if (matchName(name, "rotation"))
00991         {
00992             createRotation(ctx, att);
00993             ctx->state = BodyLeafState;
00994         }
00995         else if (matchName(name, "atmosphere"))
00996         {
00997             createAtmosphere(ctx, att);
00998             ctx->state = AtmosphereState;
00999         }
01000         else if (matchName(name, "rings"))
01001         {
01002             createRings(ctx, att);
01003             ctx->state = RingsState;
01004         }
01005         else
01006         {
01007             ctx->state = ErrorState;
01008         }
01009         break;
01010 
01011     case SurfaceState:
01012         if (matchName(name, "texture"))
01013         {
01014             createTexture(ctx, att);
01015             ctx->state = SurfaceLeafState;
01016         }
01017         else if (matchName(name, "bumpmap"))
01018         {
01019             createBumpMap(ctx, att);
01020             ctx->state = SurfaceLeafState;
01021         }
01022         else if (matchName(name, "haze"))
01023         {
01024             createHaze(ctx, att);
01025             ctx->state = SurfaceLeafState;
01026         }
01027         else
01028         {
01029             ctx->state = ErrorState;
01030         }
01031         break;
01032 
01033     case RingsState:
01034         if (matchName(name, "texture"))
01035         {
01036             createTexture(ctx, att);
01037             ctx->state = RingsLeafState;
01038         }
01039         break;
01040 
01041     case AtmosphereState:
01042         if (matchName(name, "texture"))
01043         {
01044             createTexture(ctx, att);
01045             ctx->state = AtmosphereLeafState;
01046         }
01047         break;
01048 
01049     case BodyLeafState:
01050     case SurfaceLeafState:
01051     case AtmosphereLeafState:
01052     case RingsLeafState:
01053         ctx->state = ErrorState;
01054         break;
01055 
01056     default:
01057         break;
01058     }
01059 
01060     if (ctx->state == ErrorState)
01061     {
01062         cout << "Error!  " << name << " element not expected.\n";
01063     }
01064 }
01065 
01066 
01067 // SAX endElement callback
01068 static void solarSysEndElement(void* data, const xmlChar* name)
01069 {
01070     ParserContext* ctx = reinterpret_cast<ParserContext*>(data);
01071     switch (ctx->state)
01072     {
01073     case ErrorState:
01074         return;
01075 
01076     case BodyState:
01077         if (matchName(name, "body"))
01078         {
01079             assert(ctx->body != NULL);
01080             if (ctx->body->getOrbit() == NULL)
01081             {
01082                 DPRINTF(0, "Object %s has no orbit!  Removing . . .\n",
01083                         ctx->body->getName().c_str());
01084                 
01085             }
01086             ctx->body = NULL;
01087             ctx->state = StartState;
01088         }
01089         else
01090         {
01091             ctx->state = ErrorState;
01092         }
01093         break;
01094 
01095     case SurfaceState:
01096         if (matchName(name, "surface"))
01097             ctx->state = BodyState;
01098         else
01099             ctx->state = ErrorState;
01100         break;
01101 
01102     case AtmosphereState:
01103         if (matchName(name, "atmosphere"))
01104             ctx->state = BodyState;
01105         else
01106             ctx->state = ErrorState;
01107         break;
01108 
01109     case RingsState:
01110         if (matchName(name, "rings"))
01111             ctx->state = BodyState;
01112         else
01113             ctx->state = ErrorState;
01114         break;
01115 
01116     case BodyLeafState:
01117         if (matchName(name, "geometry") ||
01118             matchName(name, "elliptical") ||
01119             matchName(name, "customorbit") ||
01120             matchName(name, "rotation"))
01121         {
01122             ctx->state = BodyState;
01123         }
01124         break;
01125 
01126     case SurfaceLeafState:
01127         if (matchName(name, "texture") ||
01128             matchName(name, "haze") ||
01129             matchName(name, "bumpmap"))
01130         {
01131             ctx->state = SurfaceState;
01132         }
01133         break;
01134 
01135     case AtmosphereLeafState:
01136         if (matchName(name, "texture"))
01137             ctx->state = AtmosphereState;
01138         break;
01139 
01140     case RingsLeafState:
01141         if (matchName(name, "texture"))
01142             ctx->state = RingsState;
01143         break;
01144 
01145     default:
01146         break;
01147     }
01148 
01149     if (ctx->state == ErrorState)
01150     {
01151         cout << "Error!  End of " << name << " element not expected.\n";
01152     }
01153 }
01154 
01155 
01156 static void initSAXHandler(xmlSAXHandler& sax)
01157 {
01158     sax = emptySAXHandler;
01159     sax.startDocument = solarSysStartDocument;
01160     sax.endDocument = solarSysEndDocument;
01161     sax.startElement = solarSysStartElement;
01162     sax.endElement = solarSysEndElement;
01163 }
01164 
01165 
01166 static bool parseSolarSystemXML(xmlSAXHandlerPtr sax,
01167                                 void* userData,
01168                                 const char* filename)
01169 {
01170     xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename);
01171     if (ctxt == NULL)
01172         return false;
01173 
01174     ctxt->sax = sax;
01175     ctxt->userData = userData;
01176 
01177     xmlParseDocument(ctxt);
01178 
01179     int wellFormed = ctxt->wellFormed;
01180     if (sax != NULL)
01181         ctxt->sax = NULL;
01182     xmlFreeParserCtxt(ctxt);
01183 
01184     cout << "Well formed: " << wellFormed << '\n';
01185 
01186     return wellFormed != 0;
01187 }
01188 
01189 
01190 bool LoadSolarSystemObjectsXML(const string& source, Universe& universe)
01191 {
01192     initSAXHandler(saxHandler);
01193 
01194     ParserContext ctx;
01195     ctx.universe = &universe;
01196 
01197     if (!parseSolarSystemXML(&saxHandler, &ctx, source.c_str()))
01198     {
01199         cout << "Error parsing " << source << '\n';
01200         return false;
01201     }
01202 
01203     return true;
01204 }

Generated on Sat Jan 14 22:30:28 2006 for Celestia by  doxygen 1.4.1