stardb.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:44k
- // stardb.cpp
- //
- // Copyright (C) 2001-2008, Chris Laurel <claurel@shatters.net>
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- #include <cstring>
- #include <cmath>
- #include <cstdlib>
- #include <cstdio>
- #include <cassert>
- #include <algorithm>
- #include <celmath/mathlib.h>
- #include <celmath/plane.h>
- #include <celutil/util.h>
- #include <celutil/bytes.h>
- #include <celengine/stardb.h>
- #include "celestia.h"
- #include "astro.h"
- #include "parser.h"
- #include "parseobject.h"
- #include "multitexture.h"
- #include "meshmanager.h"
- #include <celutil/debug.h>
- using namespace std;
- static string HDCatalogPrefix("HD ");
- static string HIPPARCOSCatalogPrefix("HIP ");
- static string GlieseCatalogPrefix("Gliese ");
- static string RossCatalogPrefix("Ross ");
- static string LacailleCatalogPrefix("Lacaille ");
- static string TychoCatalogPrefix("TYC ");
- static string SAOCatalogPrefix("SAO ");
- // The size of the root star octree node is also the maximum distance
- // distance from the Sun at which any star may be located. The current
- // setting of 1.0e7 light years is large enough to contain the entire
- // local group of galaxies. A larger value should be OK, but the
- // performance implications for octree traversal still need to be
- // investigated.
- static const float STAR_OCTREE_ROOT_SIZE = 10000000.0f;
- static const float STAR_OCTREE_MAGNITUDE = 6.0f;
- static const float STAR_EXTRA_ROOM = 0.01f; // Reserve 1% capacity for extra stars
- const char* StarDatabase::FILE_HEADER = "CELSTARS";
- const char* StarDatabase::CROSSINDEX_FILE_HEADER = "CELINDEX";
- // Used to sort stars by catalog number
- struct CatalogNumberOrderingPredicate
- {
- int unused;
- CatalogNumberOrderingPredicate() {};
- bool operator()(const Star& star0, const Star& star1) const
- {
- return (star0.getCatalogNumber() < star1.getCatalogNumber());
- }
- };
- struct CatalogNumberEquivalencePredicate
- {
- int unused;
- CatalogNumberEquivalencePredicate() {};
- bool operator()(const Star& star0, const Star& star1) const
- {
- return (star0.getCatalogNumber() == star1.getCatalogNumber());
- }
- };
- // Used to sort star pointers by catalog number
- struct PtrCatalogNumberOrderingPredicate
- {
- int unused;
- PtrCatalogNumberOrderingPredicate() {};
- bool operator()(const Star* const & star0, const Star* const & star1) const
- {
- return (star0->getCatalogNumber() < star1->getCatalogNumber());
- }
- };
- static bool parseSimpleCatalogNumber(const string& name,
- const string& prefix,
- uint32* catalogNumber)
- {
- char extra[4];
- if (compareIgnoringCase(name, prefix, prefix.length()) == 0)
- {
- unsigned int num;
- // Use scanf to see if we have a valid catalog number; it must be
- // of the form: <prefix> <non-negative integer> No additional
- // characters other than whitespace are allowed after the number.
- if (sscanf(name.c_str() + prefix.length(), " %u %c", &num, extra) == 1)
- {
- *catalogNumber = (uint32) num;
- return true;
- }
- }
- return false;
- }
- static bool parseHIPPARCOSCatalogNumber(const string& name,
- uint32* catalogNumber)
- {
- return parseSimpleCatalogNumber(name,
- HIPPARCOSCatalogPrefix,
- catalogNumber);
- }
- static bool parseHDCatalogNumber(const string& name,
- uint32* catalogNumber)
- {
- return parseSimpleCatalogNumber(name,
- HDCatalogPrefix,
- catalogNumber);
- }
- static bool parseTychoCatalogNumber(const string& name,
- uint32* catalogNumber)
- {
- if (compareIgnoringCase(name, TychoCatalogPrefix, TychoCatalogPrefix.length()) == 0)
- {
- unsigned int tyc1 = 0, tyc2 = 0, tyc3 = 0;
- if (sscanf(string(name, TychoCatalogPrefix.length(),
- string::npos).c_str(),
- " %u-%u-%u", &tyc1, &tyc2, &tyc3) == 3)
- {
- *catalogNumber = (uint32) (tyc3 * 1000000000 + tyc2 * 10000 + tyc1);
- return true;
- }
- }
- return false;
- }
- static bool parseCelestiaCatalogNumber(const string& name,
- uint32* catalogNumber)
- {
- char extra[4];
- if (name[0] == '#')
- {
- unsigned int num;
- if (sscanf(name.c_str(), "#%u %c", &num, extra) == 1)
- {
- *catalogNumber = (uint32) num;
- return true;
- }
- }
- return false;
- }
- bool StarDatabase::CrossIndexEntry::operator<(const StarDatabase::CrossIndexEntry& e) const
- {
- return catalogNumber < e.catalogNumber;
- }
- StarDatabase::StarDatabase():
- nStars (0),
- stars (NULL),
- namesDB (NULL),
- octreeRoot (NULL),
- nextAutoCatalogNumber(0xfffffffe),
- binFileCatalogNumberIndex(NULL),
- binFileStarCount(0)
- {
- crossIndexes.resize(MaxCatalog);
- }
- StarDatabase::~StarDatabase()
- {
- if (stars != NULL)
- delete [] stars;
- if (catalogNumberIndex != NULL)
- delete [] catalogNumberIndex;
- for (vector<CrossIndex*>::iterator iter = crossIndexes.begin(); iter != crossIndexes.end(); ++iter)
- {
- if (*iter != NULL)
- delete *iter;
- }
- }
- Star* StarDatabase::find(uint32 catalogNumber) const
- {
- Star refStar;
- refStar.setCatalogNumber(catalogNumber);
- Star** star = lower_bound(catalogNumberIndex,
- catalogNumberIndex + nStars,
- &refStar,
- PtrCatalogNumberOrderingPredicate());
- if (star != catalogNumberIndex + nStars && (*star)->getCatalogNumber() == catalogNumber)
- return *star;
- else
- return NULL;
- }
- uint32 StarDatabase::findCatalogNumberByName(const string& name) const
- {
- if (name.empty())
- return Star::InvalidCatalogNumber;
- uint32 catalogNumber = Star::InvalidCatalogNumber;
- if (namesDB != NULL)
- {
- catalogNumber = namesDB->findCatalogNumberByName(name);
- if (catalogNumber != Star::InvalidCatalogNumber)
- return catalogNumber;
- }
-
- if (parseCelestiaCatalogNumber(name, &catalogNumber))
- {
- return catalogNumber;
- }
- else if (parseHIPPARCOSCatalogNumber(name, &catalogNumber))
- {
- return catalogNumber;
- }
- else if (parseTychoCatalogNumber(name, &catalogNumber))
- {
- return catalogNumber;
- }
- else if (parseHDCatalogNumber(name, &catalogNumber))
- {
- return searchCrossIndexForCatalogNumber(HenryDraper, catalogNumber);
- }
- else if (parseSimpleCatalogNumber(name, SAOCatalogPrefix,
- &catalogNumber))
- {
- return searchCrossIndexForCatalogNumber(SAO, catalogNumber);
- }
- else
- {
- return Star::InvalidCatalogNumber;
- }
- }
- Star* StarDatabase::find(const string& name) const
- {
- uint32 catalogNumber = findCatalogNumberByName(name);
- if (catalogNumber != Star::InvalidCatalogNumber)
- return find(catalogNumber);
- else
- return NULL;
- }
- uint32 StarDatabase::crossIndex(const Catalog catalog, const uint32 celCatalogNumber) const
- {
- if (static_cast<uint32>(catalog) >= crossIndexes.size())
- return Star::InvalidCatalogNumber;
- CrossIndex* xindex = crossIndexes[catalog];
- if (xindex == NULL)
- return Star::InvalidCatalogNumber;
- // A simple linear search. We could store cross indices sorted by
- // both catalog numbers and trade memory for speed
- for (CrossIndex::const_iterator iter = xindex->begin(); iter != xindex->end(); iter++)
- {
- if (celCatalogNumber == iter->celCatalogNumber)
- return iter->catalogNumber;
- }
- return Star::InvalidCatalogNumber;
- }
- // Return the Celestia catalog number for the star with a specified number
- // in a cross index.
- uint32 StarDatabase::searchCrossIndexForCatalogNumber(const Catalog catalog, const uint32 number) const
- {
- if (static_cast<unsigned int>(catalog) >= crossIndexes.size())
- return Star::InvalidCatalogNumber;
- CrossIndex* xindex = crossIndexes[catalog];
- if (xindex == NULL)
- return Star::InvalidCatalogNumber;
- CrossIndexEntry xindexEnt;
- xindexEnt.catalogNumber = number;
- CrossIndex::iterator iter = lower_bound(xindex->begin(), xindex->end(),
- xindexEnt);
- if (iter == xindex->end() || iter->catalogNumber != number)
- return Star::InvalidCatalogNumber;
- else
- return iter->celCatalogNumber;
- }
- Star* StarDatabase::searchCrossIndex(const Catalog catalog, const uint32 number) const
- {
- uint32 celCatalogNumber = searchCrossIndexForCatalogNumber(catalog, number);
- if (celCatalogNumber != Star::InvalidCatalogNumber)
- return find(celCatalogNumber);
- else
- return NULL;
- }
- vector<string> StarDatabase::getCompletion(const string& name) const
- {
- vector<string> completion;
- // only named stars are supported by completion.
- if (!name.empty() && namesDB != NULL)
- return namesDB->getCompletion(name);
- else
- return completion;
- }
- static void catalogNumberToString(uint32 catalogNumber, char* buf, unsigned int bufSize)
- {
- // Just return an empty string if there's any chance that the buffer is too small
- if (bufSize < 20 && bufSize > 0)
- {
- buf[0] = ' ';
- }
- if (catalogNumber <= StarDatabase::MAX_HIPPARCOS_NUMBER)
- {
- sprintf(buf, "HIP %d", catalogNumber);
- }
- else
- {
- uint32 tyc3 = catalogNumber / 1000000000;
- catalogNumber -= tyc3 * 1000000000;
- uint32 tyc2 = catalogNumber / 10000;
- catalogNumber -= tyc2 * 10000;
- uint32 tyc1 = catalogNumber;
- sprintf(buf, "TYC %d-%d-%d", tyc1, tyc2, tyc3);
- }
- }
- // Return the name for the star with specified catalog number. The returned
- // string will be:
- // the common name if it exists, otherwise
- // the Bayer or Flamsteed designation if it exists, otherwise
- // the HD catalog number if it exists, otherwise
- // the HIPPARCOS catalog number.
- //
- // CAREFUL:
- // If the star name is not present in the names database, a new
- // string is constructed to contain the catalog number--keep in
- // mind that calling this method could possibly incur the overhead
- // of a memory allocation (though no explcit deallocation is
- // required as it's all wrapped in the string class.)
- string StarDatabase::getStarName(const Star& star, bool i18n) const
- {
- uint32 catalogNumber = star.getCatalogNumber();
- if (namesDB != NULL)
- {
- StarNameDatabase::NumberIndex::const_iterator iter = namesDB->getFirstNameIter(catalogNumber);
- if (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber)
- {
- if (i18n && iter->second != _(iter->second.c_str()))
- return _(iter->second.c_str());
- else
- return iter->second;
- }
- }
- char buf[20];
- /*
- // Get the HD catalog name
- if (star.getCatalogNumber() != Star::InvalidCatalogNumber)
- sprintf(buf, "HD %d", star.getCatalogNumber(Star::HDCatalog));
- else
- */
- catalogNumberToString(catalogNumber, buf, sizeof(buf));
- return string(buf);
- }
- // A less convenient version of getStarName that writes to a char
- // array instead of a string. The advantage is that no memory allocation
- // will every occur.
- void StarDatabase::getStarName(const Star& star, char* nameBuffer, unsigned int bufferSize, bool i18n) const
- {
- assert(bufferSize != 0);
- uint32 catalogNumber = star.getCatalogNumber();
- if (namesDB != NULL)
- {
- StarNameDatabase::NumberIndex::const_iterator iter = namesDB->getFirstNameIter(catalogNumber);
- if (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber)
- {
- if (i18n && iter->second != _(iter->second.c_str()))
- strncpy(nameBuffer, _(iter->second.c_str()), bufferSize);
- else
- strncpy(nameBuffer, iter->second.c_str(), bufferSize);
- nameBuffer[bufferSize - 1] = ' ';
- return;
- }
- }
- catalogNumberToString(catalogNumber, nameBuffer, bufferSize);
- }
- string StarDatabase::getStarNameList(const Star& star, const unsigned int maxNames) const
- {
- string starNames;
- char numString[32];
- unsigned int catalogNumber = star.getCatalogNumber();
- StarNameDatabase::NumberIndex::const_iterator iter = namesDB->getFirstNameIter(catalogNumber);
- unsigned int count = 0;
- while (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber && count < maxNames)
- {
- if (count != 0)
- starNames += " / ";
- starNames += ReplaceGreekLetterAbbr(iter->second.c_str());
- ++iter;
- ++count;
- }
- uint32 hip = catalogNumber;
- if (hip != Star::InvalidCatalogNumber && hip != 0 && count < maxNames)
- {
- if (hip <= Star::MaxTychoCatalogNumber)
- {
- if (count != 0)
- starNames += " / ";
- if (hip >= 1000000)
- {
- uint32 h = hip;
- uint32 tyc3 = h / 1000000000;
- h -= tyc3 * 1000000000;
- uint32 tyc2 = h / 10000;
- h -= tyc2 * 10000;
- uint32 tyc1 = h;
- sprintf(numString, "TYC %u-%u-%u", tyc1, tyc2, tyc3);
- starNames += numString;
- }
- else
- {
- sprintf(numString, "HIP %u", hip);
- starNames += numString;
- }
- ++count;
- }
- }
- uint32 hd = crossIndex(StarDatabase::HenryDraper, hip);
- if (count < maxNames && hd != Star::InvalidCatalogNumber)
- {
- if (count != 0)
- starNames += " / ";
- sprintf(numString, "HD %u", hd);
- starNames += numString;
- }
- uint32 sao = crossIndex(StarDatabase::SAO, hip);
- if (count < maxNames && sao != Star::InvalidCatalogNumber)
- {
- if (count != 0)
- starNames += " / ";
- sprintf(numString, "SAO %u", sao);
- starNames += numString;
- }
- return starNames;
- }
- void StarDatabase::findVisibleStars(StarHandler& starHandler,
- const Point3f& position,
- const Quatf& orientation,
- float fovY,
- float aspectRatio,
- float limitingMag) const
- {
- // Compute the bounding planes of an infinite view frustum
- Planef frustumPlanes[5];
- Vec3f planeNormals[5];
- Mat3f rot = orientation.toMatrix3();
- float h = (float) tan(fovY / 2);
- float w = h * aspectRatio;
- planeNormals[0] = Vec3f(0, 1, -h);
- planeNormals[1] = Vec3f(0, -1, -h);
- planeNormals[2] = Vec3f(1, 0, -w);
- planeNormals[3] = Vec3f(-1, 0, -w);
- planeNormals[4] = Vec3f(0, 0, -1);
- for (int i = 0; i < 5; i++)
- {
- planeNormals[i].normalize();
- planeNormals[i] = planeNormals[i] * rot;
- frustumPlanes[i] = Planef(planeNormals[i], position);
- }
- octreeRoot->processVisibleObjects(starHandler,
- position,
- frustumPlanes,
- limitingMag,
- STAR_OCTREE_ROOT_SIZE);
- }
- void StarDatabase::findCloseStars(StarHandler& starHandler,
- const Point3f& position,
- float radius) const
- {
- octreeRoot->processCloseObjects(starHandler,
- position,
- radius,
- STAR_OCTREE_ROOT_SIZE);
- }
- StarNameDatabase* StarDatabase::getNameDatabase() const
- {
- return namesDB;
- }
- void StarDatabase::setNameDatabase(StarNameDatabase* _namesDB)
- {
- namesDB = _namesDB;
- }
- bool StarDatabase::loadCrossIndex(const Catalog catalog, istream& in)
- {
- if (static_cast<unsigned int>(catalog) >= crossIndexes.size())
- return false;
- if (crossIndexes[catalog] != NULL)
- delete crossIndexes[catalog];
- // Verify that the star database file has a correct header
- {
- int headerLength = strlen(CROSSINDEX_FILE_HEADER);
- char* header = new char[headerLength];
- in.read(header, headerLength);
- if (strncmp(header, CROSSINDEX_FILE_HEADER, headerLength))
- {
- cerr << _("Bad header for cross indexn");
- return false;
- }
- delete[] header;
- }
- // Verify the version
- {
- uint16 version;
- in.read((char*) &version, sizeof version);
- LE_TO_CPU_INT16(version, version);
- if (version != 0x0100)
- {
- cerr << _("Bad version for cross indexn");
- return false;
- }
- }
- CrossIndex* xindex = new CrossIndex();
- if (xindex == NULL)
- return false;
- unsigned int record = 0;
- for (;;)
- {
- CrossIndexEntry ent;
- in.read((char *) &ent.catalogNumber, sizeof ent.catalogNumber);
- LE_TO_CPU_INT32(ent.catalogNumber, ent.catalogNumber);
- if (in.eof())
- break;
- in.read((char *) &ent.celCatalogNumber, sizeof ent.celCatalogNumber);
- LE_TO_CPU_INT32(ent.celCatalogNumber, ent.celCatalogNumber);
- if (in.fail())
- {
- cerr << _("Loading cross index failed at record ") << record << 'n';
- delete xindex;
- return false;
- }
- xindex->insert(xindex->end(), ent);
- record++;
- }
- sort(xindex->begin(), xindex->end());
- crossIndexes[catalog] = xindex;
- return true;
- }
- bool StarDatabase::loadBinary(istream& in)
- {
- uint32 nStarsInFile = 0;
- // Verify that the star database file has a correct header
- {
- int headerLength = strlen(FILE_HEADER);
- char* header = new char[headerLength];
- in.read(header, headerLength);
- if (strncmp(header, FILE_HEADER, headerLength))
- return false;
- delete[] header;
- }
- // Verify the version
- {
- uint16 version;
- in.read((char*) &version, sizeof version);
- LE_TO_CPU_INT16(version, version);
- if (version != 0x0100)
- return false;
- }
- // Read the star count
- in.read((char *) &nStarsInFile, sizeof nStarsInFile);
- LE_TO_CPU_INT32(nStarsInFile, nStarsInFile);
- if (!in.good())
- return false;
- unsigned int totalStars = nStars + nStarsInFile;
- while (((unsigned int) nStars) < totalStars)
- {
- uint32 catNo = 0;
- float x = 0.0f, y = 0.0f, z = 0.0f;
- int16 absMag;
- uint16 spectralType;
- in.read((char *) &catNo, sizeof catNo);
- LE_TO_CPU_INT32(catNo, catNo);
- in.read((char *) &x, sizeof x);
- LE_TO_CPU_FLOAT(x, x);
- in.read((char *) &y, sizeof y);
- LE_TO_CPU_FLOAT(y, y);
- in.read((char *) &z, sizeof z);
- LE_TO_CPU_FLOAT(z, z);
- in.read((char *) &absMag, sizeof absMag);
- LE_TO_CPU_INT16(absMag, absMag);
- in.read((char *) &spectralType, sizeof spectralType);
- LE_TO_CPU_INT16(spectralType, spectralType);
- if (in.bad())
- break;
- Star star;
- star.setPosition(x, y, z);
- star.setAbsoluteMagnitude((float) absMag / 256.0f);
- StarDetails* details = NULL;
- StellarClass sc;
- if (sc.unpack(spectralType))
- details = StarDetails::GetStarDetails(sc);
- if (details == NULL)
- {
- cerr << _("Bad spectral type in star database, star #") << nStars << "n";
- return false;
- }
- star.setDetails(details);
- star.setCatalogNumber(catNo);
- unsortedStars.add(star);
-
- nStars++;
- }
- if (in.bad())
- return false;
- DPRINTF(0, "StarDatabase::read: nStars = %dn", nStarsInFile);
- clog << nStars << _(" stars in binary databasen");
-
- // Create the temporary list of stars sorted by catalog number; this
- // will be used to lookup stars during file loading. After loading is
- // complete, the stars are sorted into an octree and this list gets
- // replaced.
- if (unsortedStars.size() > 0)
- {
- binFileStarCount = unsortedStars.size();
- binFileCatalogNumberIndex = new Star*[binFileStarCount];
- for (unsigned int i = 0; i < binFileStarCount; i++)
- {
- binFileCatalogNumberIndex[i] = &unsortedStars[i];
- }
- sort(binFileCatalogNumberIndex, binFileCatalogNumberIndex + binFileStarCount,
- PtrCatalogNumberOrderingPredicate());
- }
-
- return true;
- }
- void StarDatabase::finish()
- {
- clog << _("Total star count: ") << nStars << endl;
-
- buildOctree();
- buildIndexes();
- // Delete the temporary indices used only during loading
- delete binFileCatalogNumberIndex;
- stcFileCatalogNumberIndex.clear();
-
- // Resolve all barycenters; this can't be done before star sorting. There's
- // still a bug here: final orbital radii aren't available until after
- // the barycenters have been resolved, and these are required when building
- // the octree. This will only rarely cause a problem, but it still needs
- // to be addressed.
- for (vector<BarycenterUsage>::const_iterator iter = barycenters.begin();
- iter != barycenters.end(); iter++)
- {
- Star* star = find(iter->catNo);
- Star* barycenter = find(iter->barycenterCatNo);
- assert(star != NULL);
- assert(barycenter != NULL);
- if (star != NULL && barycenter != NULL)
- {
- star->setOrbitBarycenter(barycenter);
- barycenter->addOrbitingStar(star);
- }
- }
- barycenters.clear();
- }
- static void errorMessagePrelude(const Tokenizer& tok)
- {
- cerr << _("Error in .stc file (line ") << tok.getLineNumber() << "): ";
- }
- static void stcError(const Tokenizer& tok,
- const string& msg)
- {
- errorMessagePrelude(tok);
- cerr << msg << 'n';
- }
- /*! Load star data from a property list into a star instance.
- */
- bool StarDatabase::createStar(Star* star,
- StcDisposition disposition,
- uint32 catalogNumber,
- Hash* starData,
- const string& path,
- bool isBarycenter)
- {
- StarDetails* details = NULL;
- string spectralType;
- // Get the magnitude and spectral type; if the star is actually
- // a barycenter placeholder, these fields are ignored.
- if (isBarycenter)
- {
- details = StarDetails::GetBarycenterDetails();
- }
- else
- {
- if (starData->getString("SpectralType", spectralType))
- {
- StellarClass sc = StellarClass::parse(spectralType);
- details = StarDetails::GetStarDetails(sc);
- if (details == NULL)
- {
- cerr << _("Invalid star: bad spectral type.n");
- return false;
- }
- }
- else
- {
- // Spectral type is required for new stars
- if (disposition != ModifyStar)
- {
- cerr << _("Invalid star: missing spectral type.n");
- return false;
- }
- }
- }
-
- bool modifyExistingDetails = false;
- if (disposition == ModifyStar)
- {
- StarDetails* existingDetails = star->getDetails();
-
- // If we're modifying an existing star and it already has a
- // customized details record, we'll just modify that.
- if (!existingDetails->shared())
- {
- modifyExistingDetails = true;
- if (details != NULL)
- {
- // If the spectral type was modified, copy the new data
- // to the custom details record.
- existingDetails->setSpectralType(details->getSpectralType());
- existingDetails->setTemperature(details->getTemperature());
- existingDetails->setBolometricCorrection(details->getBolometricCorrection());
- if ((existingDetails->getKnowledge() & StarDetails::KnowTexture) == 0)
- existingDetails->setTexture(details->getTexture());
- if ((existingDetails->getKnowledge() & StarDetails::KnowRotation) == 0)
- existingDetails->setRotationModel(details->getRotationModel());
- existingDetails->setVisibility(details->getVisibility());
- }
-
- details = existingDetails;
- }
- else if (details == NULL)
- {
- details = existingDetails;
- }
- }
-
- string modelName;
- string textureName;
- bool hasTexture = starData->getString("Texture", textureName);
- bool hasModel = starData->getString("Mesh", modelName);
- RotationModel* rm = CreateRotationModel(starData, path, 1.0);
- bool hasRotationModel = (rm != NULL);
- Vec3d semiAxes;
- bool hasSemiAxes = starData->getVector("SemiAxes", semiAxes);
- bool hasBarycenter = false;
- Point3f barycenterPosition;
- double radius;
- bool hasRadius = starData->getNumber("Radius", radius);
-
- string infoURL;
- bool hasInfoURL = starData->getString("InfoURL", infoURL);
- Orbit* orbit = CreateOrbit(Selection(), starData, path, true);
- if (hasTexture ||
- hasModel ||
- orbit != NULL ||
- hasSemiAxes ||
- hasRadius ||
- hasRotationModel ||
- hasInfoURL)
- {
- // If the star definition has extended information, clone the
- // star details so we can customize it without affecting other
- // stars of the same spectral type.
- if (!modifyExistingDetails)
- details = new StarDetails(*details);
- if (hasTexture)
- {
- details->setTexture(MultiResTexture(textureName, path));
- details->addKnowledge(StarDetails::KnowTexture);
- }
- if (hasModel)
- {
- ResourceHandle geometryHandle = GetGeometryManager()->getHandle(GeometryInfo(modelName, path, Vec3f(0.0f, 0.0f, 0.0f), 1.0f, true));
- details->setGeometry(geometryHandle);
- }
- if (hasSemiAxes)
- {
- details->setEllipsoidSemiAxes(Vec3f((float) semiAxes.x,
- (float) semiAxes.y,
- (float) semiAxes.z));
- }
- if (hasRadius)
- {
- details->setRadius((float) radius);
- details->addKnowledge(StarDetails::KnowRadius);
- }
-
- if (hasInfoURL)
- {
- details->setInfoURL(infoURL);
- }
- if (orbit != NULL)
- {
- details->setOrbit(orbit);
- // See if a barycenter was specified as well
- uint32 barycenterCatNo = Star::InvalidCatalogNumber;
- bool barycenterDefined = false;
- string barycenterName;
- if (starData->getString("OrbitBarycenter", barycenterName))
- {
- barycenterCatNo = findCatalogNumberByName(barycenterName);
- barycenterDefined = true;
- }
- else if (starData->getNumber("OrbitBarycenter", barycenterCatNo))
- {
- barycenterDefined = true;
- }
- if (barycenterDefined)
- {
- if (barycenterCatNo != Star::InvalidCatalogNumber)
- {
- // We can't actually resolve the barycenter catalog number
- // to a Star pointer until after all stars have been loaded
- // and spatially sorted. Just store it in a list to be
- // resolved after sorting.
- BarycenterUsage bc;
- bc.catNo = catalogNumber;
- bc.barycenterCatNo = barycenterCatNo;
- barycenters.push_back(bc);
- // Even though we can't actually get the Star pointer for
- // the barycenter, we can get the star information.
- Star* barycenter = findWhileLoading(barycenterCatNo);
- if (barycenter != NULL)
- {
- hasBarycenter = true;
- barycenterPosition = barycenter->getPosition();
- }
- }
- if (!hasBarycenter)
- {
- cerr << _("Barycenter ") << barycenterName << _(" does not exist.n");
- return false;
- }
- }
- }
- if (hasRotationModel)
- details->setRotationModel(rm);
- }
- if (!modifyExistingDetails)
- star->setDetails(details);
- if (disposition != ModifyStar)
- star->setCatalogNumber(catalogNumber);
- // Compute the position in rectangular coordinates. If a star has an
- // orbit and barycenter, it's position is the position of the barycenter.
- if (hasBarycenter)
- {
- star->setPosition(barycenterPosition);
- }
- else
- {
- double ra = 0.0;
- double dec = 0.0;
- double distance = 0.0;
- if (disposition == ModifyStar)
- {
- Point3f pos = star->getPosition();
- // Convert from Celestia's coordinate system
- Vec3f v = Vec3f(pos.x, -pos.z, pos.y);
- v = v * Mat3f::xrotation((float) -astro::J2000Obliquity);
- distance = v.length();
- if (distance > 0.0)
- {
- v.normalize();
- ra = radToDeg(std::atan2(v.y, v.x));
- dec = radToDeg(std::asin(v.z));
- }
- }
-
- bool modifyPosition = false;
- if (!starData->getNumber("RA", ra))
- {
- if (disposition != ModifyStar)
- {
- cerr << _("Invalid star: missing right ascensionn");
- return false;
- }
- }
- else
- {
- modifyPosition = true;
- }
- if (!starData->getNumber("Dec", dec))
- {
- if (disposition != ModifyStar)
- {
- cerr << _("Invalid star: missing declination.n");
- return false;
- }
- }
- else
- {
- modifyPosition = true;
- }
- if (!starData->getNumber("Distance", distance))
- {
- if (disposition != ModifyStar)
- {
- cerr << _("Invalid star: missing distance.n");
- return false;
- }
- }
- else
- {
- modifyPosition = true;
- }
- // Truncate to floats to match behavior of reading from binary file.
- // The conversion to rectangular coordinates is still performed at
- // double precision, however.
- if (modifyPosition)
- {
- float raf = ((float) (ra * 24.0 / 360.0));
- float decf = ((float) dec);
- float distancef = ((float) distance);
- Point3d pos = astro::equatorialToCelestialCart((double) raf, (double) decf, (double) distancef);
- star->setPosition(Point3f((float) pos.x, (float) pos.y, (float) pos.z));
- }
- }
- if (isBarycenter)
- {
- star->setAbsoluteMagnitude(30.0f);
- }
- else
- {
- double magnitude = 0.0;
- bool magnitudeModified = true;
- if (!starData->getNumber("AbsMag", magnitude))
- {
- if (!starData->getNumber("AppMag", magnitude))
- {
- if (disposition != ModifyStar)
- {
- clog << _("Invalid star: missing magnitude.n");
- return false;
- }
- else
- {
- magnitudeModified = false;
- }
- }
- else
- {
- float distance = star->getPosition().distanceFromOrigin();
- // We can't compute the intrinsic brightness of the star from
- // the apparent magnitude if the star is within a few AU of the
- // origin.
- if (distance < 1e-5f)
- {
- clog << _("Invalid star: absolute (not apparent) magnitude must be specified for star near originn");
- return false;
- }
- magnitude = astro::appToAbsMag((float) magnitude, distance);
- }
- }
- if (magnitudeModified)
- star->setAbsoluteMagnitude((float) magnitude);
- }
- return true;
- }
- /*! Load an STC file with star definitions. Each definition has the form:
- *
- * [disposition] [object type] [catalog number] [name]
- * {
- * [properties]
- * }
- *
- * Disposition is either Add, Replace, or Modify; Add is the default.
- * Object type is either Star or Barycenter, with Star the default
- * It is an error to omit both the catalog number and the name.
- *
- * The dispositions are slightly more complicated than suggested by
- * their names. Every star must have an unique catalog number. But
- * instead of generating an error, Adding a star with a catalog
- * number that already exists will actually replace that star. Here
- * are how all of the possibilities are handled:
- *
- * <name> or <number> already exists:
- * Add <name> : new star
- * Add <number> : replace star
- * Replace <name> : replace star
- * Replace <number> : replace star
- * Modify <name> : modify star
- * Modify <number> : modify star
- *
- * <name> or <number> doesn't exist:
- * Add <name> : new star
- * Add <number> : new star
- * Replace <name> : new star
- * Replace <number> : new star
- * Modify <name> : error
- * Modify <number> : error
- */
- bool StarDatabase::load(istream& in, const string& resourcePath)
- {
- Tokenizer tokenizer(&in);
- Parser parser(&tokenizer);
- while (tokenizer.nextToken() != Tokenizer::TokenEnd)
- {
- bool isStar = true;
-
- // Parse the disposition--either Add, Replace, or Modify. The disposition
- // may be omitted. The default value is Add.
- StcDisposition disposition = AddStar;
- if (tokenizer.getTokenType() == Tokenizer::TokenName)
- {
- if (tokenizer.getNameValue() == "Modify")
- {
- disposition = ModifyStar;
- tokenizer.nextToken();
- }
- else if (tokenizer.getNameValue() == "Replace")
- {
- disposition = ReplaceStar;
- tokenizer.nextToken();
- }
- else if (tokenizer.getNameValue() == "Add")
- {
- disposition = AddStar;
- tokenizer.nextToken();
- }
- }
-
- // Parse the object type--either Star or Barycenter. The object type
- // may be omitted. The default is Star.
- if (tokenizer.getTokenType() == Tokenizer::TokenName)
- {
- if (tokenizer.getNameValue() == "Star")
- {
- isStar = true;
- }
- else if (tokenizer.getNameValue() == "Barycenter")
- {
- isStar = false;
- }
- else
- {
- stcError(tokenizer, "unrecognized object type");
- return false;
- }
- tokenizer.nextToken();
- }
- // Parse the catalog number; it may be omitted if a name is supplied.
- uint32 catalogNumber = Star::InvalidCatalogNumber;
- if (tokenizer.getTokenType() == Tokenizer::TokenNumber)
- {
- catalogNumber = (uint32) tokenizer.getNumberValue();
- tokenizer.nextToken();
- }
- string objName;
- string firstName;
- if (tokenizer.getTokenType() == Tokenizer::TokenString)
- {
- // A star name (or names) is present
- objName = tokenizer.getStringValue();
- tokenizer.nextToken();
- if (!objName.empty())
- {
- string::size_type next = objName.find(':', 0);
- firstName = objName.substr(0, next);
- }
- }
-
- Star* star = NULL;
- switch (disposition)
- {
- case AddStar:
- // Automatically generate a catalog number for the star if one isn't
- // supplied.
- if (catalogNumber == Star::InvalidCatalogNumber)
- {
- catalogNumber = nextAutoCatalogNumber--;
- }
- else
- {
- star = findWhileLoading(catalogNumber);
- }
- break;
- case ReplaceStar:
- if (catalogNumber == Star::InvalidCatalogNumber)
- {
- if (!firstName.empty())
- {
- catalogNumber = findCatalogNumberByName(firstName);
- }
- }
-
- if (catalogNumber == Star::InvalidCatalogNumber)
- {
- catalogNumber = nextAutoCatalogNumber--;
- }
- else
- {
- star = findWhileLoading(catalogNumber);
- }
- break;
-
- case ModifyStar:
- // If no catalog number was specified, try looking up the star by name
- if (catalogNumber == Star::InvalidCatalogNumber && !firstName.empty())
- {
- catalogNumber = findCatalogNumberByName(firstName);
- }
-
- if (catalogNumber != Star::InvalidCatalogNumber)
- {
- star = findWhileLoading(catalogNumber);
- }
-
- break;
- }
-
- bool isNewStar = star == NULL;
- tokenizer.pushBack();
- Value* starDataValue = parser.readValue();
- if (starDataValue == NULL)
- {
- clog << "Error reading star." << endl;
- return false;
- }
-
- if (starDataValue->getType() != Value::HashType)
- {
- DPRINTF(0, "Bad star definition.n");
- delete starDataValue;
- return false;
- }
- Hash* starData = starDataValue->getHash();
- if (isNewStar)
- star = new Star();
-
- bool ok = false;
- if (isNewStar && disposition == ModifyStar)
- {
- clog << "Modify requested for nonexistent star." << endl;
- }
- else
- {
- ok = createStar(star, disposition, catalogNumber, starData, resourcePath, !isStar);
- }
- delete starDataValue;
- if (ok)
- {
- if (isNewStar)
- {
- unsortedStars.add(*star);
- nStars++;
- delete star;
-
- // Add the new star to the temporary (load time) index.
- stcFileCatalogNumberIndex[catalogNumber] = &unsortedStars[unsortedStars.size() - 1];
- }
- if (namesDB != NULL && !objName.empty())
- {
- // List of namesDB will replace any that already exist for
- // this star.
- namesDB->erase(catalogNumber);
- // Iterate through the string for names delimited
- // by ':', and insert them into the star database.
- // Note that db->add() will skip empty namesDB.
- string::size_type startPos = 0;
- while (startPos != string::npos)
- {
- string::size_type next = objName.find(':', startPos);
- string::size_type length = string::npos;
- if (next != string::npos)
- {
- length = next - startPos;
- ++next;
- }
- string starName = objName.substr(startPos, length);
- namesDB->add(catalogNumber, starName);
- if (starName != _(starName.c_str()))
- namesDB->add(catalogNumber, _(starName.c_str()));
- startPos = next;
- }
- }
- }
- else
- {
- if (isNewStar)
- delete star;
- DPRINTF(1, "Bad star definition--will continue parsing file.n");
- }
- }
- return true;
- }
- void StarDatabase::buildOctree()
- {
- // This should only be called once for the database
- // ASSERT(octreeRoot == NULL);
- DPRINTF(1, "Sorting stars into octree . . .n");
- float absMag = astro::appToAbsMag(STAR_OCTREE_MAGNITUDE,
- STAR_OCTREE_ROOT_SIZE * (float) sqrt(3.0));
- DynamicStarOctree* root = new DynamicStarOctree(Point3f(1000, 1000, 1000),
- absMag);
- for (unsigned int i = 0; i < unsortedStars.size(); ++i)
- {
- root->insertObject(unsortedStars[i], STAR_OCTREE_ROOT_SIZE);
- }
-
- DPRINTF(1, "Spatially sorting stars for improved locality of reference . . .n");
- Star* sortedStars = new Star[nStars];
- Star* firstStar = sortedStars;
- root->rebuildAndSort(octreeRoot, firstStar);
- // ASSERT((int) (firstStar - sortedStars) == nStars);
- DPRINTF(1, "%d stars totaln", (int) (firstStar - sortedStars));
- DPRINTF(1, "Octree has %d nodes and %d stars.n",
- 1 + octreeRoot->countChildren(), octreeRoot->countObjects());
- #if PROFILE_OCTREE
- vector<OctreeLevelStatistics> stats;
- octreeRoot->computeStatistics(stats);
- for (vector<OctreeLevelStatistics>::const_iterator iter = stats.begin(); iter != stats.end(); ++iter)
- {
- int level = iter - stats.begin();
- clog << "Level " << level << ", "
- << STAR_OCTREE_ROOT_SIZE / pow(2.0, (double) level) << "ly, "
- << iter->nodeCount << " nodes, "
- << iter->objectCount << " starsn";
- }
- #endif
- // Clean up . . .
- //delete[] stars;
- unsortedStars.clear();
- delete root;
- stars = sortedStars;
- }
- void StarDatabase::buildIndexes()
- {
- // This should only be called once for the database
- // assert(catalogNumberIndexes[0] == NULL);
- DPRINTF(1, "Building catalog number indexes . . .n");
- catalogNumberIndex = new Star*[nStars];
- for (int i = 0; i < nStars; ++i)
- catalogNumberIndex[i] = &stars[i];
- sort(catalogNumberIndex, catalogNumberIndex + nStars, PtrCatalogNumberOrderingPredicate());
- }
- /*! While loading the star catalogs, this function must be called instead of
- * find(). The final catalog number index for stars cannot be built until
- * after all stars have been loaded. During catalog loading, there are two
- * separate indexes: one for the binary catalog and another index for stars
- * loaded from stc files. They binary catalog index is a sorted array, while
- * the stc catalog index is an STL map. Since the binary file can be quite
- * large, we want to avoid creating a map with as many nodes as there are
- * stars. Stc files should collectively contain many fewer stars, and stars
- * in an stc file may reference each other (barycenters). Thus, a dynamic
- * structure like a map is both practical and essential.
- */
- Star* StarDatabase::findWhileLoading(uint32 catalogNumber) const
- {
- // First check for stars loaded from the binary database
- if (binFileCatalogNumberIndex != NULL)
- {
- Star refStar;
- refStar.setCatalogNumber(catalogNumber);
-
- Star** star = lower_bound(binFileCatalogNumberIndex,
- binFileCatalogNumberIndex + binFileStarCount,
- &refStar,
- PtrCatalogNumberOrderingPredicate());
-
- if (star != binFileCatalogNumberIndex + binFileStarCount && (*star)->getCatalogNumber() == catalogNumber)
- return *star;
- }
-
- // Next check for stars loaded from an stc file
- map<uint32, Star*>::const_iterator iter = stcFileCatalogNumberIndex.find(catalogNumber);
- if (iter != stcFileCatalogNumberIndex.end())
- {
- return iter->second;
- }
-
- // Star not found
- return NULL;
- }