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

solarsys.cpp

Go to the documentation of this file.
00001 // solarsys.cpp
00002 //
00003 // Copyright (C) 2001-2004 Chris Laurel <claurel@shatters.net>
00004 //
00005 // Solar system catalog parser.
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2
00010 // of the License, or (at your option) any later version.
00011 
00012 #include <cassert>
00013 // #include <limits>
00014 #include <cstdio>
00015 
00016 #ifndef _WIN32
00017 #ifndef MACOSX_PB
00018 #include <config.h>
00019 #endif /* ! MACOSX_PB */
00020 #endif /* ! _WIN32 */
00021 
00022 #include <celutil/debug.h>
00023 #include <celmath/mathlib.h>
00024 #include <celutil/util.h>
00025 #include <cstdio>
00026 #include "astro.h"
00027 #include "parser.h"
00028 #include "texmanager.h"
00029 #include "meshmanager.h"
00030 #include "universe.h"
00031 #include "multitexture.h"
00032 #include "parseobject.h"
00033 
00034 using namespace std;
00035 
00036 
00037 enum Disposition
00038 {
00039     AddObject,
00040     ReplaceObject,
00041     ModifyObject,
00042 };
00043 
00044 
00078 static void errorMessagePrelude(const Tokenizer& tok)
00079 {
00080     cerr << _("Error in .ssc file (line ") << tok.getLineNumber() << "): ";
00081 }
00082 
00083 static void sscError(const Tokenizer& tok,
00084                      const string& msg)
00085 {
00086     errorMessagePrelude(tok);
00087     cerr << msg << '\n';
00088 }
00089 
00090 
00091 static Location* CreateLocation(Hash* locationData,
00092                                 Body* body)
00093 {
00094     Location* location = new Location();
00095 
00096     Vec3d longlat(0.0, 0.0, 0.0);
00097     locationData->getVector("LongLat", longlat);
00098 
00099     Vec3f position = body->planetocentricToCartesian((float) longlat.x,
00100                                                      (float) longlat.y,
00101                                                      (float) longlat.z);
00102     location->setPosition(position);
00103 
00104     double size = 1.0;
00105     locationData->getNumber("Size", size);
00106     location->setSize((float) size);
00107 
00108     double importance = -1.0;
00109     locationData->getNumber("Importance", importance);
00110     location->setImportance((float) importance);
00111 
00112     string featureTypeName;
00113     if (locationData->getString("Type", featureTypeName))
00114         location->setFeatureType(Location::parseFeatureType(featureTypeName));
00115 
00116     return location;
00117 }
00118 
00119 
00120 static void FillinSurface(Hash* surfaceData,
00121                           Surface* surface,
00122                           const std::string& path)
00123 {
00124     surfaceData->getColor("Color", surface->color);
00125 
00126     Color hazeColor = surface->hazeColor;
00127     float hazeDensity = hazeColor.alpha();
00128     if (surfaceData->getColor("HazeColor", hazeColor) | surfaceData->getNumber("HazeDensity", hazeDensity))
00129     {
00130         surface->hazeColor = Color(hazeColor.red(), hazeColor.green(),
00131                                    hazeColor.blue(), hazeDensity);
00132     }
00133 
00134     surfaceData->getColor("SpecularColor", surface->specularColor);
00135     surfaceData->getNumber("SpecularPower", surface->specularPower);
00136 
00137     string baseTexture;
00138     string bumpTexture;
00139     string nightTexture;
00140     string specularTexture;
00141     string normalTexture;
00142     string overlayTexture;
00143     bool applyBaseTexture = surfaceData->getString("Texture", baseTexture);
00144     bool applyBumpMap = surfaceData->getString("BumpMap", bumpTexture);
00145     bool applyNightMap = surfaceData->getString("NightTexture", nightTexture);
00146     bool separateSpecular = surfaceData->getString("SpecularTexture",
00147                                                    specularTexture);
00148     bool applyNormalMap = surfaceData->getString("NormalMap", normalTexture);
00149     bool applyOverlay = surfaceData->getString("OverlayTexture",
00150                                                overlayTexture);
00151 
00152     unsigned int baseFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
00153     unsigned int bumpFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
00154     unsigned int nightFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
00155     unsigned int specularFlags = TextureInfo::WrapTexture | TextureInfo::AllowSplitting;
00156     
00157     float bumpHeight = 2.5f;
00158     surfaceData->getNumber("BumpHeight", bumpHeight);
00159 
00160     bool blendTexture = false;
00161     surfaceData->getBoolean("BlendTexture", blendTexture);
00162 
00163     bool emissive = false;
00164     surfaceData->getBoolean("Emissive", emissive);
00165 
00166     bool compressTexture = false;
00167     surfaceData->getBoolean("CompressTexture", compressTexture);
00168     if (compressTexture)
00169         baseFlags |= TextureInfo::CompressTexture;
00170 
00171     if (blendTexture)
00172         surface->appearanceFlags |= Surface::BlendTexture;
00173     if (emissive)
00174         surface->appearanceFlags |= Surface::Emissive;
00175     if (applyBaseTexture)
00176         surface->appearanceFlags |= Surface::ApplyBaseTexture;
00177     if (applyBumpMap || applyNormalMap)
00178         surface->appearanceFlags |= Surface::ApplyBumpMap;
00179     if (applyNightMap)
00180         surface->appearanceFlags |= Surface::ApplyNightMap;
00181     if (separateSpecular)
00182         surface->appearanceFlags |= Surface::SeparateSpecularMap;
00183     if (applyOverlay)
00184         surface->appearanceFlags |= Surface::ApplyOverlay;
00185     if (surface->specularColor != Color(0.0f, 0.0f, 0.0f))
00186         surface->appearanceFlags |= Surface::SpecularReflection;
00187 
00188     if (applyBaseTexture)
00189         surface->baseTexture.setTexture(baseTexture, path, baseFlags);
00190     if (applyNightMap)
00191         surface->nightTexture.setTexture(nightTexture, path, nightFlags);
00192     if (separateSpecular)
00193         surface->specularTexture.setTexture(specularTexture, path, specularFlags);
00194 
00195     // If both are present, NormalMap overrides BumpMap
00196     if (applyNormalMap)
00197         surface->bumpTexture.setTexture(normalTexture, path, bumpFlags);
00198     else if (applyBumpMap)
00199         surface->bumpTexture.setTexture(bumpTexture, path, bumpHeight, bumpFlags);
00200 
00201     if (applyOverlay)
00202         surface->overlayTexture.setTexture(overlayTexture, path, baseFlags);
00203 }
00204 
00205 
00206 // Create a body (planet or moon) using the values from a hash
00207 // The usePlanetsUnits flags specifies whether period and semi-major axis
00208 // are in years and AU rather than days and kilometers
00209 static Body* CreatePlanet(PlanetarySystem* system,
00210                           Body* existingBody,
00211                           Hash* planetData,
00212                           const string& path,
00213                           Disposition disposition,
00214                           bool usePlanetUnits = true)
00215 {
00216     Body* body = NULL;
00217   
00218     if (disposition == ModifyObject)
00219     {
00220         body = existingBody;
00221     }
00222 
00223     if (body == NULL)
00224     {
00225         body = new Body(system);
00226     }
00227 
00228     Orbit* orbit = CreateOrbit(system, planetData, path, usePlanetUnits);
00229 
00230     if (orbit != NULL)
00231     {
00232         body->setOrbit(orbit);
00233     }
00234 
00235     if (body->getOrbit() == NULL)
00236     {
00237         DPRINTF(0, "No valid orbit specified for object '%s'; skipping . . .\n",
00238                 body->getName().c_str());
00239         delete body;
00240         return NULL;
00241     }
00242 
00243     double radius = (double)body->getRadius();
00244     planetData->getNumber("Radius", radius);
00245     body->setRadius((float) radius);
00246 
00247     int classification = body->getClassification();
00248     string classificationName;
00249     if (planetData->getString("Class", classificationName))
00250     {
00251         if (compareIgnoringCase(classificationName, "planet") == 0)
00252             classification = Body::Planet;
00253         else if (compareIgnoringCase(classificationName, "moon") == 0)
00254             classification = Body::Moon;
00255         else if (compareIgnoringCase(classificationName, "comet") == 0)
00256             classification = Body::Comet;
00257         else if (compareIgnoringCase(classificationName, "asteroid") == 0)
00258             classification = Body::Asteroid;
00259         else if (compareIgnoringCase(classificationName, "spacecraft") == 0)
00260             classification = Body::Spacecraft;
00261         else if (compareIgnoringCase(classificationName, "invisible") == 0)
00262             classification = Body::Invisible;
00263     }
00264 
00265     if (classification == Body::Unknown)
00266     {
00267         //Try to guess the type
00268         if (system->getPrimaryBody() != NULL)
00269         {
00270             if(radius > 0.1)
00271                 classification = Body::Moon;
00272             else
00273                 classification = Body::Spacecraft;
00274         }
00275         else
00276         {
00277             if(radius < 1000.0)
00278                 classification = Body::Asteroid;
00279             else
00280                 classification = Body::Planet;
00281         }
00282     }
00283     body->setClassification(classification);
00284 
00285     // g++ is missing limits header, so we can use this
00286     // double beginning   = -numeric_limits<double>::infinity();
00287     // double ending      =  numeric_limits<double>::infinity();
00288     double beginning   = -1.0e+50;
00289     double ending      =  1.0e+50;
00290     body->getLifespan(beginning, ending);
00291     ParseDate(planetData, "Beginning", beginning);
00292     ParseDate(planetData, "Ending", ending);
00293     body->setLifespan(beginning, ending);
00294 
00295     string infoURL;
00296     if (planetData->getString("InfoURL", infoURL))
00297         body->setInfoURL(infoURL);
00298     
00299     double albedo = 0.5;
00300     if (planetData->getNumber("Albedo", albedo))
00301         body->setAlbedo((float) albedo);
00302 
00303     double oblateness = 0.0;
00304     if (planetData->getNumber("Oblateness", oblateness))
00305         body->setOblateness((float) oblateness);
00306     
00307     double mass = 0.0;
00308     if (planetData->getNumber("Mass", mass))
00309         body->setMass((float) mass);
00310 
00311     Quatf orientation;
00312     if (planetData->getRotation("Orientation", orientation))
00313         body->setOrientation(orientation);
00314 
00315     RotationElements re = body->getRotationElements();
00316     if (disposition != ModifyObject)
00317         re.period = (float) body->getOrbit()->getPeriod();
00318     FillinRotationElements(planetData, re);
00319     body->setRotationElements(re);
00320 
00321     Surface surface;
00322     if (disposition == ModifyObject)
00323     {
00324         surface = body->getSurface();
00325     }
00326     else
00327     {
00328         surface.color = Color(1.0f, 1.0f, 1.0f);
00329         surface.hazeColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
00330     }
00331     FillinSurface(planetData, &surface, path);
00332     body->setSurface(surface);
00333 
00334     {
00335         string model("");
00336         if (planetData->getString("Mesh", model))
00337         {
00338             Vec3f modelCenter(0.0f, 0.0f, 0.0f);
00339             if (planetData->getVector("MeshCenter", modelCenter))
00340             {
00341                 // TODO: Adjust bounding radius if model center isn't
00342                 // (0.0f, 0.0f, 0.0f)
00343             }
00344 
00345             ResourceHandle modelHandle = GetModelManager()->getHandle(ModelInfo(model, path, modelCenter));
00346             body->setModel(modelHandle);
00347 
00348         }
00349     }
00350 
00351     // Read the atmosphere
00352     {
00353         Value* atmosDataValue = planetData->getValue("Atmosphere");
00354         if (atmosDataValue != NULL)
00355         {
00356             if (atmosDataValue->getType() != Value::HashType)
00357             {
00358                 cout << "ReadSolarSystem: Atmosphere must be an assoc array.\n";
00359             }
00360             else
00361             {
00362                 Hash* atmosData = atmosDataValue->getHash();
00363                 assert(atmosData != NULL);
00364                 
00365                 Atmosphere* atmosphere = NULL;
00366                 if (disposition == ModifyObject)
00367                 {
00368                     atmosphere = body->getAtmosphere();
00369                     if (atmosphere == NULL)
00370                     {
00371                         Atmosphere atm;
00372                         body->setAtmosphere(atm);
00373                         atmosphere = body->getAtmosphere();
00374                     }
00375                 }
00376                 else
00377                 {
00378                     atmosphere = new Atmosphere();
00379                 }
00380                 atmosData->getNumber("Height", atmosphere->height);
00381                 atmosData->getColor("Lower", atmosphere->lowerColor);
00382                 atmosData->getColor("Upper", atmosphere->upperColor);
00383                 atmosData->getColor("Sky", atmosphere->skyColor);
00384                 atmosData->getColor("Sunset", atmosphere->sunsetColor);
00385                 atmosData->getNumber("CloudHeight", atmosphere->cloudHeight);
00386                 if (atmosData->getNumber("CloudSpeed", atmosphere->cloudSpeed))
00387                     atmosphere->cloudSpeed = degToRad(atmosphere->cloudSpeed);
00388 
00389                 string cloudTexture;
00390                 if (atmosData->getString("CloudMap", cloudTexture))
00391                 {
00392                     atmosphere->cloudTexture.setTexture(cloudTexture,
00393                                                         path,
00394                                                         TextureInfo::WrapTexture);
00395                 }
00396 
00397                 body->setAtmosphere(*atmosphere);
00398                 if (disposition != ModifyObject)
00399                     delete atmosphere;
00400             }
00401 
00402             delete atmosDataValue;
00403         }
00404     }
00405 
00406     // Read the ring system
00407     {
00408         Value* ringsDataValue = planetData->getValue("Rings");
00409         if (ringsDataValue != NULL)
00410         {
00411             if (ringsDataValue->getType() != Value::HashType)
00412             {
00413                 cout << "ReadSolarSystem: Rings must be an assoc array.\n";
00414             }
00415             else
00416             {
00417                 Hash* ringsData = ringsDataValue->getHash();
00418                 // ASSERT(ringsData != NULL);
00419 
00420                 RingSystem rings(0.0f, 0.0f);
00421                 if (body->getRings() != NULL)
00422                     rings = *body->getRings();
00423 
00424                 double inner = 0.0, outer = 0.0;
00425                 if (ringsData->getNumber("Inner", inner))
00426                     rings.innerRadius = (float) inner;
00427                 if (ringsData->getNumber("Outer", outer))
00428                     rings.outerRadius = (float) outer;
00429 
00430                 Color color(1.0f, 1.0f, 1.0f);
00431                 if (ringsData->getColor("Color", color))
00432                     rings.color = color;
00433 
00434                 string textureName;
00435                 if (ringsData->getString("Texture", textureName))
00436                     rings.texture = MultiResTexture(textureName, path);
00437 
00438                 body->setRings(rings);
00439             }
00440 
00441             delete ringsDataValue;
00442         }
00443     }
00444 
00445     return body;
00446 }
00447 
00448 
00449 bool LoadSolarSystemObjects(istream& in,
00450                             Universe& universe,
00451                             const std::string& directory)
00452 {
00453     Tokenizer tokenizer(&in); 
00454     Parser parser(&tokenizer);
00455 
00456     while (tokenizer.nextToken() != Tokenizer::TokenEnd)
00457     {
00458         // Read the disposition; if none is specified, the default is Add.
00459         Disposition disposition = AddObject;
00460         if (tokenizer.getTokenType() == Tokenizer::TokenName)
00461         {
00462             if (tokenizer.getNameValue() == "Add")
00463             {
00464                 disposition = AddObject;
00465                 tokenizer.nextToken();
00466             }
00467             else if (tokenizer.getNameValue() == "Replace")
00468             {
00469                 disposition = ReplaceObject;
00470                 tokenizer.nextToken();
00471             }
00472             else if (tokenizer.getNameValue() == "Modify")
00473             {
00474                 disposition = ModifyObject;
00475                 tokenizer.nextToken();
00476             }
00477         }
00478 
00479         // Read the item type; if none is specified the default is Body
00480         string itemType("Body");
00481         if (tokenizer.getTokenType() == Tokenizer::TokenName)
00482         {
00483             itemType = tokenizer.getNameValue();
00484             tokenizer.nextToken();
00485         }
00486 
00487         if (tokenizer.getTokenType() != Tokenizer::TokenString)
00488         {
00489             sscError(tokenizer, "object name expected");
00490             return false;
00491         }
00492         string name = tokenizer.getStringValue().c_str();
00493 
00494         if (tokenizer.nextToken() != Tokenizer::TokenString)
00495         {
00496             sscError(tokenizer, "bad parent object name");
00497             return false;
00498         }
00499         string parentName = tokenizer.getStringValue().c_str();
00500 
00501         Value* objectDataValue = parser.readValue();
00502         if (objectDataValue == NULL)
00503         {
00504             sscError(tokenizer, "bad object definition");
00505             return false;
00506         }
00507 
00508         if (objectDataValue->getType() != Value::HashType)
00509         {
00510             sscError(tokenizer, "{ expected");
00511             return false;
00512         }
00513         Hash* objectData = objectDataValue->getHash();
00514 
00515         Selection parent = universe.findPath(parentName, NULL, 0);
00516         PlanetarySystem* parentSystem = NULL;
00517 
00518         if (itemType == "Body")
00519         {
00520             bool orbitsPlanet = false;
00521             if (parent.star() != NULL)
00522             {
00523                 SolarSystem* solarSystem = universe.getSolarSystem(parent.star());
00524                 if (solarSystem == NULL)
00525                 {
00526                     // No solar system defined for this star yet, so we need
00527                     // to create it.
00528                     solarSystem = universe.createSolarSystem(parent.star());
00529                 }
00530                 parentSystem = solarSystem->getPlanets();
00531             }
00532             else if (parent.body() != NULL)
00533             {
00534                 // Parent is a planet or moon
00535                 parentSystem = parent.body()->getSatellites();
00536                 if (parentSystem == NULL)
00537                 {
00538                     // If the planet doesn't already have any satellites, we
00539                     // have to create a new planetary system for it.
00540                     parentSystem = new PlanetarySystem(parent.body());
00541                     parent.body()->setSatellites(parentSystem);
00542                 }
00543                 orbitsPlanet = true;
00544             }
00545             else
00546             {
00547                 errorMessagePrelude(tokenizer);
00548                 cerr << _("parent body '") << parentName << _("' of '") << name << _("' not found.\n");
00549             }
00550 
00551             if (parentSystem != NULL)
00552             {
00553                 Body* existingBody = parentSystem->find(name);
00554                 if (existingBody && disposition == AddObject)
00555                 {
00556                     errorMessagePrelude(tokenizer);
00557                     cerr << _("warning duplicate definition of ") <<
00558                         parentName << " " <<  name << '\n';
00559                 }
00560                 
00561                 Body* body = CreatePlanet(parentSystem, existingBody, objectData, directory, disposition, !orbitsPlanet);
00562                 if (body != NULL)
00563                 {
00564                     body->setName(name);
00565                     if (disposition == ReplaceObject)
00566                     {
00567                         parentSystem->replaceBody(existingBody, body);
00568                         delete existingBody;
00569                     } 
00570                     else if (disposition == AddObject)
00571                     {
00572                         parentSystem->addBody(body);
00573                     }
00574                 }
00575             }
00576         }
00577         else if (itemType == "AltSurface")
00578         {
00579             Surface* surface = new Surface();
00580             surface->color = Color(1.0f, 1.0f, 1.0f);
00581             surface->hazeColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
00582             FillinSurface(objectData, surface, directory);
00583             if (surface != NULL && parent.body() != NULL)
00584                 parent.body()->addAlternateSurface(name, surface);
00585             else
00586                 sscError(tokenizer, _("bad alternate surface"));
00587         }
00588         else if (itemType == "Location")
00589         {
00590             if (parent.body() != NULL)
00591             {
00592                 Location* location = CreateLocation(objectData, parent.body());
00593                 if (location != NULL)
00594                 {
00595                     location->setName(name);
00596                     parent.body()->addLocation(location);
00597                 }
00598                 else
00599                 {
00600                     sscError(tokenizer, _("bad location"));
00601                 }
00602             }
00603             else
00604             {
00605                 errorMessagePrelude(tokenizer);
00606                 cerr << _("parent body '") << parentName << _("' of '") << name << _("' not found.\n");
00607             }
00608         }
00609     }
00610 
00611     // TODO: Return some notification if there's an error parsing the file
00612     return true;
00613 }
00614 
00615 
00616 SolarSystem::SolarSystem(Star* _star) : star(_star)
00617 {
00618     planets = new PlanetarySystem(_star);
00619 }
00620 
00621 
00622 Star* SolarSystem::getStar() const
00623 {
00624     return star;
00625 }
00626 
00627 Point3f SolarSystem::getCenter() const
00628 {
00629     // TODO: This is a very simple method at the moment, but it will get
00630     // more complex when planets around multistar systems are supported
00631     // where the planets may orbit the center of mass of two stars.
00632     return star->getPosition();
00633 }
00634 
00635 PlanetarySystem* SolarSystem::getPlanets() const
00636 {
00637     return planets;
00638 }

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