00001
00002
00003
00004
00005
00006
00007
00008
00009
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;
00042
00043
00044 const char* DSODatabase::FILE_HEADER = "CEL_DSOs";
00045
00046
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;
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
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
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();
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
00287 if (nDSOs == capacity)
00288 {
00289
00290
00291
00292
00293 capacity = (int) (capacity * 1.05);
00294
00295
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
00321
00322 namesDB->erase(objCatalogNumber);
00323
00324
00325
00326
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
00367
00368
00369
00370
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);
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
00395
00396
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
00411
00412 if (DSOmag > DSO_DEFAULT_ABS_MAGNITUDE)
00413 avgAbsMag += DSOmag;
00414 else if (nDSOeff > 1)
00415 nDSOeff--;
00416
00417 }
00418 avgAbsMag /= (double) nDSOeff;
00419
00420 }
00421
00422
00423 void DSODatabase::buildIndexes()
00424 {
00425
00426
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 }