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

dsodb.cpp

Go to the documentation of this file.
00001 //
00002 // C++ Implementation: dsodb
00003 //
00004 // Description:
00005 //
00006 //
00007 // Author: Toti <root@totibox>, (C) 2005
00008 //
00009 // Copyright: See COPYING file that comes with this distribution
00010 //
00011 //
00012 
00013 #include <cmath>
00014 #include <cstdlib>
00015 #include <cstdio>
00016 #include <cassert>
00017 #include <algorithm>
00018 #include <celmath/mathlib.h>
00019 #include <celmath/plane.h>
00020 #include <celutil/util.h>
00021 #include <celutil/bytes.h>
00022 #include <celutil/utf8.h>
00023 #include <celengine/dsodb.h>
00024 #include "celestia.h"
00025 #include "astro.h"
00026 #include "parser.h"
00027 #include "parseobject.h"
00028 #include "multitexture.h"
00029 #include "meshmanager.h"
00030 #include <celutil/debug.h>
00031 
00032 #include <celengine/galaxy.h>
00033 #include <celengine/opencluster.h>
00034 #include <celengine/nebula.h>
00035 
00036 using namespace std;
00037 
00038 
00039 static const float DSO_OCTREE_ROOT_SIZE   = 1.8e9f;
00040 static const float DSO_OCTREE_MAGNITUDE   = 12.0f;
00041 static const float DSO_EXTRA_ROOM         = 0.01f; // Reserve 1% capacity for extra DSOs
00042                                                    // (useful as a complement of binary loaded DSOs)
00043 
00044 const char* DSODatabase::FILE_HEADER      = "CEL_DSOs";
00045 
00046 // Used to sort DSO pointers by catalog number
00047 struct PtrCatalogNumberOrderingPredicate
00048 {
00049     int unused;
00050 
00051     PtrCatalogNumberOrderingPredicate() {};
00052 
00053     bool operator()(const DeepSkyObject* const & dso0, const DeepSkyObject* const & dso1) const
00054     {
00055         return (dso0->getCatalogNumber() < dso1->getCatalogNumber());
00056     }
00057 };
00058 
00059 
00060 DSODatabase::DSODatabase():
00061     nDSOs                (0),
00062     capacity             (0),
00063     DSOs                 (NULL),
00064     namesDB              (NULL),
00065     octreeRoot           (NULL),
00066     nextAutoCatalogNumber(0xfffffffe),
00067     avgAbsMag            (0)
00068 {
00069 }
00070 
00071 
00072 DSODatabase::~DSODatabase()
00073 {
00074     if (DSOs != NULL)
00075         delete [] DSOs;
00076 
00077     if (catalogNumberIndex != NULL)
00078         delete [] catalogNumberIndex;
00079 }
00080 
00081 
00082 DeepSkyObject* DSODatabase::find(const uint32 catalogNumber) const
00083 {
00084     Galaxy refDSO;  //terrible hack !!
00085     refDSO.setCatalogNumber(catalogNumber);
00086 
00087     DeepSkyObject** dso   = lower_bound(catalogNumberIndex,
00088                                         catalogNumberIndex + nDSOs,
00089                                         &refDSO,
00090                                         PtrCatalogNumberOrderingPredicate());
00091 
00092     if (dso != catalogNumberIndex + nDSOs && (*dso)->getCatalogNumber() == catalogNumber)
00093         return *dso;
00094     else
00095         return NULL;
00096 }
00097 
00098 
00099 DeepSkyObject* DSODatabase::find(const string& name) const
00100 {
00101     if (name.empty())
00102         return NULL;
00103 
00104     if (namesDB != NULL)
00105     {
00106         uint32 catalogNumber   = namesDB->findCatalogNumberByName(name);
00107         if (catalogNumber != DeepSkyObject::InvalidCatalogNumber)
00108             return find(catalogNumber);
00109     }
00110 
00111     return NULL;
00112 }
00113 
00114 
00115 vector<string> DSODatabase::getCompletion(const string& name) const
00116 {
00117     vector<string> completion;
00118 
00119     // only named DSOs are supported by completion.
00120     if (!name.empty() && namesDB != NULL)
00121         return namesDB->getCompletion(name);
00122     else
00123         return completion;
00124 }
00125 
00126 
00127 string DSODatabase::getDSOName(const DeepSkyObject* const & dso) const
00128 {
00129     uint32 catalogNumber    = dso->getCatalogNumber();
00130 
00131     if (namesDB != NULL)
00132     {
00133         DSONameDatabase::NumberIndex::const_iterator iter   = namesDB->getFirstNameIter(catalogNumber);
00134         if (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber)
00135         {
00136             return iter->second;
00137         }
00138     }
00139 
00140     return "";
00141 }
00142 
00143 
00144 string DSODatabase::getDSONameList(const DeepSkyObject* const & dso, const unsigned int maxNames) const
00145 {
00146     string dsoNames;
00147 
00148     unsigned int catalogNumber   = dso->getCatalogNumber();
00149 
00150     DSONameDatabase::NumberIndex::const_iterator iter  = namesDB->getFirstNameIter(catalogNumber);
00151 
00152     unsigned int count = 0;
00153     while (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber && count < maxNames)
00154     {
00155         if (count != 0)
00156             dsoNames   += " / ";
00157 
00158         dsoNames   += iter->second;
00159         ++iter;
00160         ++count;
00161     }
00162 
00163     return dsoNames;
00164 }
00165 
00166 
00167 void DSODatabase::findVisibleDSOs(DSOHandler&    dsoHandler,
00168                                   const Point3d& obsPos,
00169                                   const Quatf&   obsOrient,
00170                                   float fovY,
00171                                   float aspectRatio,
00172                                   float limitingMag) const
00173 {
00174     // Compute the bounding planes of an infinite view frustum
00175     Planef frustumPlanes[5];
00176     Vec3f  planeNormals[5];
00177 
00178     Mat3f  rot    = obsOrient.toMatrix3();
00179     float  h      = (float) tan(fovY / 2);
00180     float  w      = h * aspectRatio;
00181 
00182     planeNormals[0] = Vec3f(0,  1, -h);
00183     planeNormals[1] = Vec3f(0, -1, -h);
00184     planeNormals[2] = Vec3f(1,  0, -w);
00185     planeNormals[3] = Vec3f(-1, 0, -w);
00186     planeNormals[4] = Vec3f(0,  0, -1);
00187 
00188     for (int i=0; i<5; ++i)
00189     {
00190         planeNormals[i].normalize();                        //TODO: prenormalize ?
00191         planeNormals[i]    = planeNormals[i] * rot;
00192         frustumPlanes[i]   = Planef(planeNormals[i], Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z) );
00193     }
00194 
00195     octreeRoot->processVisibleObjects(dsoHandler,
00196                                       obsPos,
00197                                       frustumPlanes,
00198                                       limitingMag,
00199                                       DSO_OCTREE_ROOT_SIZE);
00200 }
00201 
00202 
00203 void DSODatabase::findCloseDSOs(DSOHandler&    dsoHandler,
00204                                 const Point3d& obsPos,
00205                                 float radius) const
00206 {
00207     octreeRoot->processCloseObjects(dsoHandler,
00208                                     obsPos,
00209                                     radius,
00210                                     DSO_OCTREE_ROOT_SIZE);
00211 }
00212 
00213 
00214 DSONameDatabase* DSODatabase::getNameDatabase() const
00215 {
00216     return namesDB;
00217 }
00218 
00219 
00220 void DSODatabase::setNameDatabase(DSONameDatabase* _namesDB)
00221 {
00222     namesDB    = _namesDB;
00223 }
00224 
00225 
00226 bool DSODatabase::load(istream& in, const string& resourcePath)
00227 {
00228     Tokenizer tokenizer(&in);
00229     Parser    parser(&tokenizer);
00230 
00231     while (tokenizer.nextToken() != Tokenizer::TokenEnd)
00232     {
00233         string objType;
00234         string objName;
00235 
00236         if (tokenizer.getTokenType() != Tokenizer::TokenName)
00237         {
00238             DPRINTF(0, "Error parsing deep sky catalog file.\n");
00239             return false;
00240         }
00241         objType = tokenizer.getNameValue();
00242 
00243         bool   autoGenCatalogNumber   = true;
00244         uint32 objCatalogNumber       = DeepSkyObject::InvalidCatalogNumber;
00245         if (tokenizer.getTokenType() == Tokenizer::TokenNumber)
00246         {
00247             autoGenCatalogNumber   = false;
00248             objCatalogNumber          = (uint32) tokenizer.getNumberValue();
00249             tokenizer.nextToken();
00250         }
00251 
00252         if (autoGenCatalogNumber)
00253         {
00254             objCatalogNumber   = nextAutoCatalogNumber--;
00255         }
00256 
00257         if (tokenizer.nextToken() != Tokenizer::TokenString)
00258         {
00259             DPRINTF(0, "Error parsing deep sky catalog file: bad name.\n");
00260             return false;
00261         }
00262         objName = tokenizer.getStringValue();
00263 
00264         Value* objParamsValue    = parser.readValue();
00265         if (objParamsValue == NULL ||
00266             objParamsValue->getType() != Value::HashType)
00267         {
00268             DPRINTF(0, "Error parsing deep sky catalog entry %s\n", objName.c_str());
00269             return false;
00270         }
00271 
00272         Hash* objParams    = objParamsValue->getHash();
00273         assert(objParams != NULL);
00274 
00275         DeepSkyObject* obj = NULL;
00276         if (compareIgnoringCase(objType, "Galaxy") == 0)
00277             obj = new Galaxy();
00278         else if (compareIgnoringCase(objType, "Nebula") == 0)
00279             obj = new Nebula();
00280         else if (compareIgnoringCase(objType, "OpenCluster") == 0)
00281             obj = new OpenCluster();
00282 
00283 
00284         if (obj != NULL && obj->load(objParams, resourcePath))
00285         {
00286             // Ensure that the DSO array is large enough
00287             if (nDSOs == capacity)
00288             {
00289                 // Grow the array by 5%--this may be too little, but the
00290                 // assumption here is that there will be small numbers of
00291                 // DSOs in text files added to a big collection loaded from
00292                 // a binary file.
00293                 capacity    = (int) (capacity * 1.05);
00294 
00295                 // 100 DSOs seems like a reasonable minimum
00296                 if (capacity < 100)
00297                     capacity   = 100;
00298 
00299                 DeepSkyObject** newDSOs   = new DeepSkyObject*[capacity];
00300                 if (newDSOs == NULL)
00301                 {
00302                     DPRINTF(0, "Out of memory!");
00303                     return false;
00304                 }
00305 
00306                 if (DSOs != NULL)
00307                 {
00308                     copy(DSOs, DSOs + nDSOs, newDSOs);
00309                     delete[] DSOs;
00310                 }
00311                 DSOs   = newDSOs;
00312             }
00313 
00314             DSOs[nDSOs++]    = obj;
00315 
00316             obj->setCatalogNumber(objCatalogNumber);
00317 
00318             if (namesDB != NULL && !objName.empty())
00319             {
00320                 // List of names will replace any that already exist for
00321                 // this DSO.
00322                 namesDB->erase(objCatalogNumber);
00323 
00324                 // Iterate through the string for names delimited
00325                 // by ':', and insert them into the DSO database.
00326                 // Note that db->add() will skip empty names.
00327                 string::size_type startPos   = 0;
00328                 while (startPos != string::npos)
00329                 {
00330                     string::size_type next    = objName.find(':', startPos);
00331                     string::size_type length  = string::npos;
00332                     if (next != string::npos)
00333                     {
00334                         length   = next - startPos;
00335                         ++next;
00336                     }
00337                     namesDB->add(objCatalogNumber, objName.substr(startPos, length));
00338                     startPos   = next;
00339                 }
00340             }
00341         }
00342         else
00343         {
00344             DPRINTF(1, "Bad Deep Sky Object definition--will continue parsing file.\n");
00345             if (objParamsValue != NULL)
00346                 delete objParamsValue;
00347             return false;
00348         }
00349     }
00350     return true;
00351 }
00352 
00353 
00354 bool DSODatabase::loadBinary(istream& in)
00355 {
00356     return true;
00357 }
00358 
00359 
00360 void DSODatabase::finish()
00361 {
00362     buildOctree();
00363     buildIndexes();
00364     calcAvgAbsMag();
00365     /*
00366     // Put AbsMag = avgAbsMag for Add-ons without AbsMag entry
00367     for (int i = 0; i < nDSOs; ++i)
00368     {
00369         if(DSOs[i]->getAbsoluteMagnitude() == DSO_DEFAULT_ABS_MAGNITUDE)
00370             DSOs[i]->setAbsoluteMagnitude((float)avgAbsMag);
00371     }
00372     */
00373 }
00374 
00375 
00376 void DSODatabase::buildOctree()
00377 {
00378     DPRINTF(1, "Sorting DSOs into octree . . .\n");
00379     float absMag             = astro::appToAbsMag(DSO_OCTREE_MAGNITUDE, DSO_OCTREE_ROOT_SIZE * (float) sqrt(3.0));
00380     DynamicDSOOctree* root   = new DynamicDSOOctree(Point3d(1000, 1000, 1000), absMag);        //TODO: center??
00381     for (int i = 0; i < nDSOs; ++i)
00382     {
00383         root->insertObject(DSOs[i], DSO_OCTREE_ROOT_SIZE);
00384     }
00385 
00386     DPRINTF(1, "Spatially sorting DSOs for improved locality of reference . . .\n");
00387     DeepSkyObject** sortedDSOs    = new DeepSkyObject*[nDSOs];
00388     DeepSkyObject** firstDSO      = sortedDSOs;
00389     root->rebuildAndSort(octreeRoot, firstDSO);
00390 
00391     DPRINTF(1, "%d DSOs total\n", (int) (firstDSO - sortedDSOs));
00392     DPRINTF(1, "Octree has %d nodes and %d DSOs.\n",
00393             1 + octreeRoot->countChildren(), octreeRoot->countObjects());
00394     //cout<<"DSOs:  "<< octreeRoot->countObjects()<<"   Nodes:"
00395     //    <<octreeRoot->countChildren() <<endl;
00396     // Clean up . . .
00397     delete[] DSOs;
00398     delete   root;
00399 
00400     DSOs = sortedDSOs;
00401 }
00402 
00403 void DSODatabase::calcAvgAbsMag()
00404 {
00405     uint32 nDSOeff = size();
00406     for (int i = 0; i < nDSOs; ++i)
00407     {
00408         double DSOmag = DSOs[i]->getAbsoluteMagnitude();
00409 
00410         // take only DSO's with realistic AbsMag entry
00411         // (> DSO_DEFAULT_ABS_MAGNITUDE) into account
00412         if (DSOmag > DSO_DEFAULT_ABS_MAGNITUDE)
00413             avgAbsMag += DSOmag;
00414         else if (nDSOeff > 1)
00415             nDSOeff--;
00416         //cout << nDSOs<<"  "<<DSOmag<<"  "<<nDSOeff<<endl;
00417     }
00418     avgAbsMag /= (double) nDSOeff;
00419     //cout<<avgAbsMag<<endl;
00420 }
00421 
00422 
00423 void DSODatabase::buildIndexes()
00424 {
00425     // This should only be called once for the database
00426     // assert(catalogNumberIndexes[0] == NULL);
00427 
00428     DPRINTF(1, "Building catalog number indexes . . .\n");
00429 
00430     catalogNumberIndex = new DeepSkyObject*[nDSOs];
00431     for (int i = 0; i < nDSOs; ++i)
00432         catalogNumberIndex[i] = DSOs[i];
00433 
00434     sort(catalogNumberIndex, catalogNumberIndex + nDSOs, PtrCatalogNumberOrderingPredicate());
00435 }
00436 
00437 
00438 double DSODatabase::getAverageAbsoluteMagnitude() const
00439 {
00440     return avgAbsMag;
00441 }

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