00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include <cstdio>
00017 #include <iostream>
00018 #include <fstream>
00019 #include <iomanip>
00020 #include <algorithm>
00021 #include <cstdlib>
00022 #include <cctype>
00023 #include <cstring>
00024 #include <cassert>
00025 #include <ctime>
00026 #include <celengine/gl.h>
00027 #include <celmath/vecmath.h>
00028 #include <celmath/quaternion.h>
00029 #include <celmath/mathlib.h>
00030 #include <celutil/util.h>
00031 #include <celutil/filetype.h>
00032 #include <celutil/directory.h>
00033 #include <celutil/formatnum.h>
00034 #include <celengine/astro.h>
00035 #include <celengine/overlay.h>
00036 #include <celengine/console.h>
00037 #include <celengine/execution.h>
00038 #include <celengine/cmdparser.h>
00039
00040 #include <celengine/multitexture.h>
00041 #include "favorites.h"
00042 #include "celestiacore.h"
00043 #include <celutil/debug.h>
00044 #include <celutil/utf8.h>
00045 #include "url.h"
00046
00047 using namespace std;
00048
00049 static const int DragThreshold = 3;
00050
00051
00052 static const double timeScaleFactor = 10.0f;
00053 static const double fMinSlewRate = 3.0;
00054 static const double fMaxKeyAccel = 20.0;
00055 static const float fAltitudeThreshold = 4.0f;
00056 static const float RotationBraking = 10.0f;
00057 static const float RotationDecay = 2.0f;
00058 static const double MaximumTimeRate = 1.0e15;
00059 static const float stdFOV = degToRad(45.0f);
00060 static const float MaximumFOV = degToRad(120.0f);
00061 static const float MinimumFOV = degToRad(0.001f);
00062 static float KeyRotationAccel = degToRad(120.0f);
00063 static float MouseRotationSensitivity = degToRad(1.0f);
00064
00065 static const int ConsolePageRows = 10;
00066 static Console console(200, 120);
00067
00068
00069 static void warning(string s)
00070 {
00071 cout << s;
00072 }
00073
00074
00075 struct OverlayImage
00076 {
00077 Texture* texture;
00078 int xSize;
00079 int ySize;
00080 int left;
00081 int bottom;
00082 };
00083
00084 vector<OverlayImage> overlayImages;
00085
00086
00087
00088
00089 class CoreExecutionEnvironment : public ExecutionEnvironment
00090 {
00091 private:
00092 CelestiaCore& core;
00093
00094 public:
00095 CoreExecutionEnvironment(CelestiaCore& _core) : core(_core)
00096 {
00097 }
00098
00099 Simulation* getSimulation() const
00100 {
00101 return core.getSimulation();
00102 }
00103
00104 Renderer* getRenderer() const
00105 {
00106 return core.getRenderer();
00107 }
00108
00109 CelestiaCore* getCelestiaCore() const
00110 {
00111 return &core;
00112 }
00113
00114 void showText(string s, int horig, int vorig, int hoff, int voff,
00115 double duration)
00116 {
00117 core.showText(s, horig, vorig, hoff, voff, duration);
00118 }
00119 };
00120
00121
00122
00123
00124
00125
00126
00127 float ComputeRotationCoarseness(Simulation& sim)
00128 {
00129 float coarseness = 1.5f;
00130
00131 Selection selection = sim.getActiveObserver()->getFrame().refObject;
00132 if (selection.getType() == Selection::Type_Star ||
00133 selection.getType() == Selection::Type_Body)
00134 {
00135 double radius = selection.radius();
00136 double t = sim.getTime();
00137 UniversalCoord observerPosition = sim.getActiveObserver()->getPosition();
00138 UniversalCoord selectionPosition = selection.getPosition(t);
00139 double distance = astro::microLightYearsToKilometers(observerPosition.distanceTo(selectionPosition));
00140 double altitude = distance - radius;
00141 if (altitude > 0.0 && altitude < radius)
00142 {
00143 coarseness *= (float) max(0.01, altitude / radius);
00144 }
00145 }
00146
00147 return coarseness;
00148 }
00149
00150
00151 View::View(View::Type _type,
00152 Observer* _observer,
00153 float _x, float _y,
00154 float _width, float _height) :
00155 type(_type),
00156 observer(_observer),
00157 parent(0),
00158 child1(0),
00159 child2(0),
00160 x(_x),
00161 y(_y),
00162 width(_width),
00163 height(_height),
00164 renderFlags(0),
00165 labelMode(0),
00166 zoom(1),
00167 alternateZoom(1)
00168 {
00169 }
00170
00171 void View::mapWindowToView(float wx, float wy, float& vx, float& vy) const
00172 {
00173 vx = (wx - x) / width;
00174 vy = (wy + (y + height - 1)) / height;
00175 vx = (vx - 0.5f) * (width / height);
00176 vy = 0.5f - vy;
00177 }
00178
00179 void View::walkTreeResize(View* sibling, int sign) {
00180 float ratio;
00181 switch (parent->type)
00182 {
00183 case View::HorizontalSplit:
00184 ratio = parent->height / (parent->height - height);
00185 sibling->height *= ratio;
00186 if (sign == 1)
00187 {
00188 sibling->y = parent->y + (sibling->y - parent->y) * ratio;
00189 }
00190 else
00191 {
00192 sibling->y = parent->y + (sibling->y - (y + height)) * ratio;
00193 }
00194 break;
00195
00196 case View::VerticalSplit:
00197 ratio = parent->width / (parent->width - width);
00198 sibling->width *= ratio;
00199 if (sign == 1)
00200 {
00201 sibling->x = parent->x + (sibling->x - parent->x) * ratio;
00202 }
00203 else
00204 {
00205 sibling->x = parent->x + (sibling->x - (x + width) ) * ratio;
00206 }
00207 break;
00208 case View::ViewWindow:
00209 break;
00210 }
00211 if (sibling->child1) walkTreeResize(sibling->child1, sign);
00212 if (sibling->child2) walkTreeResize(sibling->child2, sign);
00213 }
00214
00215 bool View::walkTreeResizeDelta(View* v, float delta, bool check)
00216 {
00217 View *p=v;
00218 int sign = -1;
00219 float ratio;
00220 double newSize;
00221
00222 if (v->child1)
00223 {
00224 if (!walkTreeResizeDelta(v->child1, delta, check))
00225 return false;
00226 }
00227
00228 if (v->child2)
00229 {
00230 if (!walkTreeResizeDelta(v->child2, delta, check))
00231 return false;
00232 }
00233
00234 while ( p != child1 && p != child2 && (p = p->parent) ) ;
00235 if (p == child1) sign = 1;
00236 switch (type)
00237 {
00238 case View::HorizontalSplit:
00239 delta = -delta;
00240 ratio = (p->height + sign * delta) / p->height;
00241 newSize = v->height * ratio;
00242 if (newSize <= .1) return false;
00243 if (check) return true;
00244 v->height = (float) newSize;
00245 if (sign == 1)
00246 {
00247 v->y = p->y + (v->y - p->y) * ratio;
00248 }
00249 else
00250 {
00251 v->y = p->y + delta + (v->y - p->y) * ratio;
00252 }
00253 break;
00254
00255 case View::VerticalSplit:
00256 ratio = (p->width + sign * delta) / p->width;
00257 newSize = v->width * ratio;
00258 if (newSize <= .1) return false;
00259 if (check) return true;
00260 v->width = (float) newSize;
00261 if (sign == 1)
00262 {
00263 v->x = p->x + (v->x - p->x) * ratio;
00264 }
00265 else
00266 {
00267 v->x = p->x + delta + (v->x - p->x) * ratio;
00268 }
00269 break;
00270 case View::ViewWindow:
00271 break;
00272 }
00273
00274 return true;
00275 }
00276
00277
00278 CelestiaCore::CelestiaCore() :
00279 config(NULL),
00280 universe(NULL),
00281 favorites(NULL),
00282 destinations(NULL),
00283 sim(NULL),
00284 renderer(NULL),
00285 overlay(NULL),
00286 width(1),
00287 height(1),
00288 font(NULL),
00289 titleFont(NULL),
00290 messageText(""),
00291 messageHOrigin(0),
00292 messageVOrigin(0),
00293 messageHOffset(0),
00294 messageVOffset(0),
00295 messageStart(0.0),
00296 messageDuration(0.0),
00297 typedText(""),
00298 typedTextCompletionIdx(-1),
00299 textEnterMode(KbNormal),
00300 hudDetail(1),
00301 wireframe(false),
00302 editMode(false),
00303 altAzimuthMode(false),
00304 showConsole(false),
00305 lightTravelFlag(false),
00306 flashFrameStart(0.0),
00307 timer(NULL),
00308 runningScript(NULL),
00309 execEnv(NULL),
00310 #ifdef CELX
00311 celxScript(NULL),
00312 #endif
00313 timeZoneBias(0),
00314 showFPSCounter(false),
00315 nFrames(0),
00316 fps(0.0),
00317 fpsCounterStartTime(0.0),
00318 oldFOV(stdFOV),
00319 mouseMotion(0.0f),
00320 dollyMotion(0.0),
00321 dollyTime(0.0),
00322 zoomMotion(0.0),
00323 zoomTime(0.0),
00324 sysTime(0.0),
00325 currentTime(0.0),
00326 timeScale(1.0),
00327 paused(false),
00328 scriptPaused(false),
00329 joystickRotation(0.0f, 0.0f, 0.0f),
00330 KeyAccel(1.0),
00331 movieCapture(NULL),
00332 recording(false),
00333 contextMenuCallback(NULL),
00334 logoTexture(NULL),
00335 alerter(NULL),
00336 cursorHandler(NULL),
00337 defaultCursorShape(CelestiaCore::CrossCursor),
00338 historyCurrent(0),
00339 activeView(0),
00340 showActiveViewFrame(false),
00341 showViewFrames(true),
00342 resizeSplit(0),
00343 screenDpi(96),
00344 distanceToScreen(400)
00345 {
00346
00347
00348
00349 renderer = new Renderer();
00350 timer = CreateTimer();
00351
00352 execEnv = new CoreExecutionEnvironment(*this);
00353
00354 int i;
00355 for (i = 0; i < KeyCount; i++)
00356 {
00357 keysPressed[i] = false;
00358 shiftKeysPressed[i] = false;
00359 }
00360 for (i = 0; i < JoyButtonCount; i++)
00361 joyButtonsPressed[i] = false;
00362
00363 clog.rdbuf(console.rdbuf());
00364 cerr.rdbuf(console.rdbuf());
00365 console.setWindowHeight(ConsolePageRows);
00366 }
00367
00368 CelestiaCore::~CelestiaCore()
00369 {
00370 if (movieCapture != NULL)
00371 recordEnd();
00372 delete execEnv;
00373 }
00374
00375 void CelestiaCore::readFavoritesFile()
00376 {
00377
00378 if (config->favoritesFile != "")
00379 {
00380 ifstream in(config->favoritesFile.c_str(), ios::in);
00381
00382 if (in.good())
00383 {
00384 favorites = ReadFavoritesList(in);
00385 if (favorites == NULL)
00386 {
00387 warning(_("Error reading favorites file."));
00388 }
00389 }
00390 }
00391 }
00392
00393 void CelestiaCore::writeFavoritesFile()
00394 {
00395 if (config->favoritesFile != "")
00396 {
00397 ofstream out(config->favoritesFile.c_str(), ios::out);
00398 if (out.good())
00399 WriteFavoritesList(*favorites, out);
00400 }
00401 }
00402
00403 void CelestiaCore::activateFavorite(FavoritesEntry& fav)
00404 {
00405 sim->cancelMotion();
00406 sim->setTime(fav.jd);
00407 sim->setObserverPosition(fav.position);
00408 sim->setObserverOrientation(fav.orientation);
00409 sim->setSelection(sim->findObjectFromPath(fav.selectionName));
00410 sim->setFrame(fav.coordSys, sim->getSelection());
00411 }
00412
00413 void CelestiaCore::addFavorite(string name, string parentFolder, FavoritesList::iterator* iter)
00414 {
00415 FavoritesList::iterator pos;
00416 if(!iter)
00417 pos = favorites->end();
00418 else
00419 pos = *iter;
00420 FavoritesEntry* fav = new FavoritesEntry();
00421 fav->jd = sim->getTime();
00422 fav->position = sim->getObserver().getPosition();
00423 fav->orientation = sim->getObserver().getOrientation();
00424 fav->name = name;
00425 fav->isFolder = false;
00426 fav->parentFolder = parentFolder;
00427 fav->selectionName = sim->getSelection().getName();
00428 fav->coordSys = sim->getFrame().coordSys;
00429
00430 favorites->insert(pos, fav);
00431 }
00432
00433 void CelestiaCore::addFavoriteFolder(string name, FavoritesList::iterator* iter)
00434 {
00435 FavoritesList::iterator pos;
00436 if(!iter)
00437 pos = favorites->end();
00438 else
00439 pos = *iter;
00440 FavoritesEntry* fav = new FavoritesEntry();
00441 fav->name = name;
00442 fav->isFolder = true;
00443
00444 favorites->insert(pos, fav);
00445 }
00446
00447 FavoritesList* CelestiaCore::getFavorites()
00448 {
00449 return favorites;
00450 }
00451
00452
00453 const DestinationList* CelestiaCore::getDestinations()
00454 {
00455 return destinations;
00456 }
00457
00458
00459
00460 void showSelectionInfo(const Selection& sel)
00461 {
00462 Vec3f axis(0.0f, 1.0, 0.0f);
00463 float angle = 0.0f;
00464
00465 if (sel.deepsky() != NULL)
00466 sel.deepsky()->getOrientation().getAxisAngle(axis, angle);
00467 else if (sel.body() != NULL)
00468 sel.body()->getOrientation().getAxisAngle(axis, angle);
00469
00470 cout << sel.getName() << '\n';
00471 cout << _("Orientation: ") << '[' << axis.x << ',' << axis.y << ',' << axis.z << "], " << radToDeg(angle) << '\n';
00472 }
00473
00474
00475 void CelestiaCore::cancelScript()
00476 {
00477 if (runningScript != NULL)
00478 {
00479 delete runningScript;
00480 scriptPaused = false;
00481 runningScript = NULL;
00482 }
00483 #ifdef CELX
00484 if (celxScript != NULL)
00485 {
00486 celxScript->cleanup();
00487 if (textEnterMode & KbPassToScript)
00488 setTextEnterMode(textEnterMode & ~KbPassToScript);
00489 delete celxScript;
00490 celxScript = NULL;
00491 scriptPaused = false;
00492 }
00493 #endif
00494 }
00495
00496
00497 void CelestiaCore::runScript(CommandSequence* script)
00498 {
00499 cancelScript();
00500 if (runningScript == NULL && script != NULL)
00501 runningScript = new Execution(*script, *execEnv);
00502 }
00503
00504
00505 void CelestiaCore::runScript(const string& filename)
00506 {
00507 cancelScript();
00508 ContentType type = DetermineFileType(filename);
00509
00510 if (type == Content_CelestiaLegacyScript)
00511 {
00512 ifstream scriptfile(filename.c_str());
00513 if (!scriptfile.good())
00514 {
00515 if (alerter != NULL)
00516 alerter->fatalError(_("Error opening script file."));
00517 else
00518 flash(_("Error opening script file."));
00519 }
00520 else
00521 {
00522 CommandParser parser(scriptfile);
00523 CommandSequence* script = parser.parse();
00524 if (script == NULL)
00525 {
00526 const vector<string>* errors = parser.getErrors();
00527 const char* errorMsg = "";
00528 if (errors->size() > 0)
00529 errorMsg = (*errors)[0].c_str();
00530 if (alerter != NULL)
00531 alerter->fatalError(errorMsg);
00532 else
00533 flash(errorMsg);
00534 }
00535 else
00536 {
00537 runningScript = new Execution(*script, *execEnv);
00538 }
00539 }
00540 }
00541 #ifdef CELX
00542 else if (type == Content_CelestiaScript)
00543 {
00544 ifstream scriptfile(filename.c_str());
00545 if (!scriptfile.good())
00546 {
00547 char errMsg[1024];
00548 sprintf(errMsg, _("Error opening script '%s'"), filename.c_str());
00549 if (alerter != NULL)
00550 alerter->fatalError(errMsg);
00551 else
00552 flash(errMsg);
00553 }
00554
00555 celxScript = new LuaState();
00556 celxScript->init(this);
00557 int status = celxScript->loadScript(scriptfile, filename);
00558 if (status != 0)
00559 {
00560 string errMsg = celxScript->getErrorMessage();
00561 if (errMsg.empty())
00562 errMsg = _("Unknown error opening script");
00563 if (alerter != NULL)
00564 alerter->fatalError(errMsg);
00565 else
00566 flash(errMsg);
00567
00568 delete celxScript;
00569 celxScript = NULL;
00570 }
00571 else
00572 {
00573
00574
00575 if (!celxScript->createThread())
00576 {
00577 const char* errMsg = _("Script coroutine initialization failed");
00578 if (alerter != NULL)
00579 alerter->fatalError(errMsg);
00580 else
00581 flash(errMsg);
00582 delete celxScript;
00583 celxScript = NULL;
00584 }
00585 }
00586 }
00587 #endif
00588 else
00589 {
00590 if (alerter != NULL)
00591 alerter->fatalError(_("Invalid filetype"));
00592 else
00593 flash(_("Invalid filetype"));
00594 }
00595 }
00596
00597
00598 bool checkMask(int modifiers, int mask)
00599 {
00600 return (modifiers & mask) == mask;
00601 }
00602
00603 void CelestiaCore::mouseButtonDown(float x, float y, int button)
00604 {
00605 mouseMotion = 0.0f;
00606
00607 if (views.size() > 1) {
00608
00609 pickView(x, y);
00610 }
00611
00612 if (views.size() > 1 && button == LeftButton)
00613 {
00614 View *v1 = 0, *v2 = 0;
00615 for (vector<View*>::iterator i = views.begin(); i != views.end(); i++)
00616 {
00617 View* v = *i;
00618 float vx, vy, vxp, vyp;
00619 vx = ( x / width - v->x ) / v->width;
00620 vy = ( (1 - y / height ) - v->y ) / v->height;
00621 vxp = vx * v->width * width;
00622 vyp = vy * v->height * height;
00623 if ( vx >=0 && vx <= 1 && ( abs(vyp) <= 2 || abs(vyp - v->height * height) <= 2)
00624 || vy >=0 && vy <= 1 && ( abs(vxp) <= 2 || abs(vxp - v->width * width) <= 2) )
00625 {
00626 if (v1 == 0)
00627 {
00628 v1 = v;
00629 }
00630 else
00631 {
00632 v2 = v;
00633 break;
00634 }
00635 }
00636 }
00637 if (v2 != 0) {
00638
00639 View *p1 = v1, *p2 = v2;
00640 while ( (p1 = p1->parent) )
00641 {
00642 p2 = v2;
00643 while ( (p2 = p2->parent) && p1 != p2) ;
00644 if (p2 != 0) break;
00645 }
00646 if (p2 != 0)
00647 {
00648 resizeSplit = p1;
00649 }
00650 }
00651 }
00652
00653 }
00654
00655 void CelestiaCore::mouseButtonUp(float x, float y, int button)
00656 {
00657
00658
00659 float pickTolerance = sim->getActiveObserver()->getFOV() / height * 4.0f;
00660
00661 if (resizeSplit)
00662 {
00663 resizeSplit = 0;
00664 return;
00665 }
00666
00667
00668
00669
00670 if (mouseMotion < DragThreshold)
00671 {
00672 if (button == LeftButton)
00673 {
00674 pickView(x, y);
00675
00676 float pickX, pickY;
00677 float aspectRatio = ((float) width / (float) height);
00678 views[activeView]->mapWindowToView((float) x / (float) width,
00679 (float) y / (float) height,
00680 pickX, pickY);
00681 Vec3f pickRay =
00682 sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY);
00683
00684 Selection oldSel = sim->getSelection();
00685 Selection newSel = sim->pickObject(pickRay, pickTolerance);
00686 addToHistory();
00687 sim->setSelection(newSel);
00688 if (!oldSel.empty() && oldSel == newSel)
00689 sim->centerSelection();
00690 }
00691 else if (button == RightButton)
00692 {
00693 float pickX, pickY;
00694 float aspectRatio = ((float) width / (float) height);
00695 views[activeView]->mapWindowToView((float) x / (float) width,
00696 (float) y / (float) height,
00697 pickX, pickY);
00698 Vec3f pickRay =
00699 sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY);
00700
00701 Selection sel = sim->pickObject(pickRay, pickTolerance);
00702 if (!sel.empty())
00703 {
00704 if (contextMenuCallback != NULL)
00705 contextMenuCallback(x, y, sel);
00706 }
00707 }
00708 else if (button == MiddleButton)
00709 {
00710 if (views[activeView]->zoom != 1)
00711 {
00712 views[activeView]->alternateZoom = views[activeView]->zoom;
00713 views[activeView]->zoom = 1;
00714 }
00715 else
00716 {
00717 views[activeView]->zoom = views[activeView]->alternateZoom;
00718 }
00719 setFOVFromZoom();
00720
00721
00722 if((renderer->getRenderFlags() & Renderer::ShowAutoMag) != 0)
00723 setFaintestAutoMag();
00724 }
00725 }
00726 }
00727
00728 void CelestiaCore::mouseWheel(float motion, int modifiers)
00729 {
00730 if (motion != 0.0)
00731 {
00732 if ((modifiers & ShiftKey) != 0)
00733 {
00734 zoomTime = currentTime;
00735 zoomMotion = 0.25f * motion;
00736 }
00737 else
00738 {
00739 dollyTime = currentTime;
00740 dollyMotion = 0.25f * motion;
00741 }
00742 }
00743 }
00744
00748 void CelestiaCore::mouseMove(float x, float y)
00749 {
00750 if (views.size() > 1 && cursorHandler != NULL)
00751 {
00752 View* v1 = 0;
00753 View* v2 = 0;
00754
00755 for (vector<View*>::iterator i = views.begin(); i != views.end(); i++)
00756 {
00757 View* v = *i;
00758 float vx, vy, vxp, vyp;
00759 vx = (x / width - v->x) / v->width;
00760 vy = ((1 - y / height) - v->y ) / v->height;
00761 vxp = vx * v->width * width;
00762 vyp = vy * v->height * height;
00763
00764 if (vx >=0 && vx <= 1 && (abs(vyp) <= 2 || abs(vyp - v->height * height) <= 2))
00765 {
00766 cursorHandler->setCursorShape(CelestiaCore::SizeVerCursor);
00767 return;
00768 }
00769 else if (vy >=0 && vy <= 1 && (abs(vxp) <= 2 || abs(vxp - v->width * width) <= 2))
00770 {
00771 cursorHandler->setCursorShape(CelestiaCore::SizeHorCursor);
00772 return;
00773 }
00774 }
00775 cursorHandler->setCursorShape(defaultCursorShape);
00776 }
00777 return;
00778 }
00779
00780 void CelestiaCore::mouseMove(float dx, float dy, int modifiers)
00781 {
00782 if (resizeSplit != 0)
00783 {
00784 switch(resizeSplit->type) {
00785 case View::HorizontalSplit:
00786 if ( resizeSplit->walkTreeResizeDelta(resizeSplit->child1, dy / height, true)
00787 && resizeSplit->walkTreeResizeDelta(resizeSplit->child2, dy / height, true))
00788 {
00789 resizeSplit->walkTreeResizeDelta(resizeSplit->child1, dy / height, false);
00790 resizeSplit->walkTreeResizeDelta(resizeSplit->child2, dy / height, false);
00791 }
00792 break;
00793 case View::VerticalSplit:
00794 if ( resizeSplit->walkTreeResizeDelta(resizeSplit->child1, dx / width, true)
00795 && resizeSplit->walkTreeResizeDelta(resizeSplit->child2, dx / width, true)
00796 )
00797 {
00798 resizeSplit->walkTreeResizeDelta(resizeSplit->child1, dx / width, false);
00799 resizeSplit->walkTreeResizeDelta(resizeSplit->child2, dx / width, false);
00800 }
00801 break;
00802 case View::ViewWindow:
00803 break;
00804 }
00805 setFOVFromZoom();
00806 return;
00807 }
00808 if ((modifiers & (LeftButton | RightButton)) != 0)
00809 {
00810 if (editMode && checkMask(modifiers, LeftButton | ShiftKey | ControlKey))
00811 {
00812
00813 Selection sel = sim->getSelection();
00814 Quatf q(1);
00815 if (sel.getType() == Selection::Type_DeepSky)
00816 q = sel.deepsky()->getOrientation();
00817 else if (sel.getType() == Selection::Type_Body)
00818 q = sel.body()->getOrientation();
00819
00820 q.yrotate(dx / width);
00821 q.xrotate(dy / height);
00822
00823 if (sel.getType() == Selection::Type_DeepSky)
00824 sel.deepsky()->setOrientation(q);
00825 else if (sel.getType() == Selection::Type_Body)
00826 sel.body()->setOrientation(q);
00827 }
00828 else if (editMode && checkMask(modifiers, RightButton | ShiftKey | ControlKey))
00829 {
00830
00831
00832 Selection sel = sim->getSelection();
00833 if (sel.deepsky() != NULL)
00834 {
00835 double t = sim->getTime();
00836 Vec3d v = sel.getPosition(t) - sim->getObserver().getPosition();
00837 Vec3f axis((float) v.x, (float) v.y, (float) v.z);
00838 axis.normalize();
00839
00840 Quatf r;
00841 r.setAxisAngle(axis, dx / width);
00842
00843 Quatf q = sel.deepsky()->getOrientation();
00844 sel.deepsky()->setOrientation(r * q);
00845 }
00846 }
00847 else if (checkMask(modifiers, LeftButton | RightButton) ||
00848 checkMask(modifiers, LeftButton | ControlKey))
00849 {
00850
00851
00852 float amount = dy / height;
00853 sim->changeOrbitDistance(amount * 5);
00854 if (dx * dx > dy * dy)
00855 {
00856 Observer& observer = sim->getObserver();
00857 Vec3d v = Vec3d(0, 0, dx * -MouseRotationSensitivity);
00858 RigidTransform rt = observer.getSituation();
00859 Quatd dr = 0.5 * (v * rt.rotation);
00860 rt.rotation += dr;
00861 rt.rotation.normalize();
00862 observer.setSituation(rt);
00863 }
00864 }
00865 else if (checkMask(modifiers, LeftButton | ShiftKey))
00866 {
00867
00868 float amount = dy / height;
00869 float minFOV = MinimumFOV;
00870 float maxFOV = MaximumFOV;
00871 float fov = sim->getActiveObserver()->getFOV();
00872
00873 if (fov < minFOV)
00874 fov = minFOV;
00875
00876
00877
00878 float newFOV = minFOV + (float) exp(log(fov - minFOV) + amount * 4);
00879 if (newFOV < minFOV)
00880 newFOV = minFOV;
00881 else if (newFOV > maxFOV)
00882 newFOV = maxFOV;
00883 sim->getActiveObserver()->setFOV(newFOV);
00884 setZoomFromFOV();
00885
00886 if ((renderer->getRenderFlags() & Renderer::ShowAutoMag))
00887 {
00888 setFaintestAutoMag();
00889 char buf[128];
00890 sprintf(buf, _("Magnitude limit: %.2f"), sim->getFaintestVisible());
00891 flash(buf);
00892 }
00893 }
00894 else
00895 {
00896 Quatf q(1);
00897
00898 float coarseness = 1.5f;
00899 if ((modifiers & RightButton) == 0)
00900 {
00901 coarseness = radToDeg(sim->getActiveObserver()->getFOV()) / 30.0f;
00902 }
00903 else
00904 {
00905
00906
00907 coarseness = ComputeRotationCoarseness(*sim);
00908 }
00909 q.yrotate(dx / width * coarseness);
00910 q.xrotate(dy / height * coarseness);
00911 if ((modifiers & RightButton) != 0)
00912 sim->orbit(q);
00913 else
00914 sim->rotate(~q);
00915 }
00916
00917 mouseMotion += abs(dy) + abs(dx);
00918 }
00919 }
00920
00922 void CelestiaCore::pickView(float x, float y)
00923 {
00924 if (x+2 < views[activeView]->x * width || x-2 > (views[activeView]->x + views[activeView]->width) * width
00925 || (height - y)+2 < views[activeView]->y * height || (height - y)-2 > (views[activeView]->y + views[activeView]->height) * height)
00926 {
00927 vector<View*>::iterator i = views.begin();
00928 int n = 0;
00929 while (i < views.end() && (x+2 < (*i)->x * width || x-2 > ((*i)->x + (*i)->width) * width
00930 || (height - y)+2 < (*i)->y * height || (height - y)-2 > ((*i)->y + (*i)->height) * height))
00931 {
00932 i++; n++;
00933 }
00934 activeView = n;
00935 sim->setActiveObserver(views[activeView]->observer);
00936 if (!showActiveViewFrame)
00937 flashFrameStart = currentTime;
00938 return;
00939 }
00940 }
00941
00942 void CelestiaCore::joystickAxis(int axis, float amount)
00943 {
00944 float deadZone = 0.25f;
00945
00946 if (abs(amount) < deadZone)
00947 amount = 0.0f;
00948 else
00949 amount = (amount - deadZone) * (1.0f / (1.0f - deadZone));
00950
00951 amount = sign(amount) * square(amount);
00952
00953 if (axis == Joy_XAxis)
00954 joystickRotation.y = amount;
00955 else if (axis == Joy_YAxis)
00956 joystickRotation.x = -amount;
00957 }
00958
00959
00960 void CelestiaCore::joystickButton(int button, bool down)
00961 {
00962 if (button >= 0 && button < JoyButtonCount)
00963 joyButtonsPressed[button] = down;
00964 }
00965
00966
00967 static void scrollConsole(Console& con, int lines)
00968 {
00969 int topRow = con.getWindowRow();
00970 int height = con.getHeight();
00971
00972 if (lines < 0)
00973 {
00974 if (topRow + lines > -height)
00975 console.setWindowRow(topRow + lines);
00976 else
00977 console.setWindowRow(-(height - 1));
00978 }
00979 else
00980 {
00981 if (topRow + lines <= -ConsolePageRows)
00982 console.setWindowRow(topRow + lines);
00983 else
00984 console.setWindowRow(-ConsolePageRows);
00985 }
00986 }
00987
00988
00989 void CelestiaCore::keyDown(int key, int modifiers)
00990 {
00991 switch (key)
00992 {
00993 case Key_F1:
00994 sim->setTargetSpeed(0);
00995 break;
00996 case Key_F2:
00997 sim->setTargetSpeed(astro::kilometersToMicroLightYears(1.0f));
00998 break;
00999 case Key_F3:
01000 sim->setTargetSpeed(astro::kilometersToMicroLightYears(1000.0f));
01001 break;
01002 case Key_F4:
01003 sim->setTargetSpeed((float) astro::kilometersToMicroLightYears(astro::speedOfLight));
01004 break;
01005 case Key_F5:
01006 sim->setTargetSpeed((float) astro::kilometersToMicroLightYears(astro::speedOfLight * 10.0));
01007 break;
01008 case Key_F6:
01009 sim->setTargetSpeed(astro::AUtoMicroLightYears(1.0f));
01010 break;
01011 case Key_F7:
01012 sim->setTargetSpeed(1e6);
01013 break;
01014 case Key_F11:
01015 if (movieCapture != NULL)
01016 {
01017 if (isRecording())
01018 recordPause();
01019 else
01020 recordBegin();
01021 }
01022 break;
01023 case Key_F12:
01024 if (movieCapture != NULL)
01025 recordEnd();
01026 break;
01027 case Key_NumPad2:
01028 case Key_NumPad4:
01029 case Key_NumPad6:
01030 case Key_NumPad7:
01031 case Key_NumPad8:
01032 case Key_NumPad9:
01033 sim->setTargetSpeed(sim->getTargetSpeed());
01034 break;
01035
01036 case Key_Down:
01037 if (showConsole)
01038 scrollConsole(console, 1);
01039 break;
01040
01041 case Key_Up:
01042 if (showConsole)
01043 scrollConsole(console, -1);
01044 break;
01045
01046 case Key_PageDown:
01047 if (showConsole)
01048 scrollConsole(console, ConsolePageRows);
01049 break;
01050
01051 case Key_PageUp:
01052 if (showConsole)
01053 scrollConsole(console, -ConsolePageRows);
01054 break;
01055 }
01056
01057 if (KeyAccel < fMaxKeyAccel)
01058 KeyAccel *= 1.1;
01059
01060
01061 if (islower(key))
01062 key = toupper(key);
01063 if (!(key >= 'A' && key <= 'Z' && (textEnterMode != KbNormal) ))
01064 {
01065 if (modifiers & ShiftKey)
01066 shiftKeysPressed[key] = true;
01067 else
01068 keysPressed[key] = true;
01069 }
01070 }
01071
01072 void CelestiaCore::keyUp(int key, int)
01073 {
01074 KeyAccel = 1.0;
01075 if (islower(key))
01076 key = toupper(key);
01077 keysPressed[key] = false;
01078 shiftKeysPressed[key] = false;
01079 }
01080
01081 void CelestiaCore::charEntered(char c, int modifiers)
01082 {
01083 char C[2];
01084 C[0] = c;
01085 C[1] = '\0';
01086 charEntered(C, modifiers);
01087 }
01088
01089 void CelestiaCore::charEntered(const char *c_p, int modifiers)
01090 {
01091 Observer* observer = sim->getActiveObserver();
01092
01093 char c = *c_p;
01094
01095
01096 #ifdef CELX
01097 if (celxScript != NULL && (textEnterMode & KbPassToScript))
01098 {
01099 if (c != '\033' && celxScript->charEntered(c_p))
01100 {
01101 return;
01102 }
01103 }
01104 #endif
01105
01106 if (textEnterMode & KbAutoComplete)
01107 {
01108 wchar_t wc = NULL;
01109 UTF8Decode(c_p, 0, strlen(c_p), wc);
01110 #ifdef MACOSX
01111 if ( wc && (isalpha(wc) || isdigit(wc) || ispunct(c) || c == ' ') )
01112 #else
01113 if ( wc && (iswalpha(wc) || iswdigit(wc) || iswpunct(c) || c == ' ') )
01114 #endif
01115 {
01116 typedText += string(c_p);
01117 typedTextCompletion = sim->getObjectCompletion(typedText, (renderer->getLabelMode() & Renderer::LocationLabels) != 0);
01118 typedTextCompletionIdx = -1;
01119 #ifdef AUTO_COMPLETION
01120 if (typedTextCompletion.size() == 1)
01121 {
01122 string::size_type pos = typedText.rfind('/', typedText.length());
01123 if (pos != string::npos)
01124 typedText = typedText.substr(0, pos + 1) + typedTextCompletion[0];
01125 else
01126 typedText = typedTextCompletion[0];
01127 }
01128 #endif
01129 }
01130 else if (c == '\b')
01131 {
01132 typedTextCompletionIdx = -1;
01133 if (typedText.size() > 0)
01134 {
01135 #ifdef AUTO_COMPLETION
01136 do
01137 {
01138 #endif
01139
01140
01141 while (typedText.size() && ((typedText[typedText.size() - 1] & 0xC0) == 0x80)) {
01142 typedText = string(typedText, 0, typedText.size() - 1);
01143 }
01144
01145 typedText = string(typedText, 0, typedText.size() - 1);
01146 if (typedText.size() > 0)
01147 {
01148 typedTextCompletion = sim->getObjectCompletion(typedText, (renderer->getLabelMode() & Renderer::LocationLabels) != 0);
01149 } else {
01150 typedTextCompletion.clear();
01151 }
01152 #ifdef AUTO_COMPLETION
01153 } while (typedText.size() > 0 && typedTextCompletion.size() == 1);
01154 #endif
01155 }
01156 }
01157 else if (c == '\011')
01158 {
01159 if (typedTextCompletionIdx + 1 < (int) typedTextCompletion.size())
01160 typedTextCompletionIdx++;
01161 else if ((int) typedTextCompletion.size() > 0 && typedTextCompletionIdx + 1 == (int) typedTextCompletion.size())
01162 typedTextCompletionIdx = 0;
01163 if (typedTextCompletionIdx >= 0) {
01164 string::size_type pos = typedText.rfind('/', typedText.length());
01165 if (pos != string::npos)
01166 typedText = typedText.substr(0, pos + 1) + typedTextCompletion[typedTextCompletionIdx];
01167 else
01168 typedText = typedTextCompletion[typedTextCompletionIdx];
01169 }
01170 }
01171 else if (c == Key_BackTab)
01172 {
01173 if (typedTextCompletionIdx > 0)
01174 typedTextCompletionIdx--;
01175 else if (typedTextCompletionIdx == 0)
01176 typedTextCompletionIdx = typedTextCompletion.size() - 1;
01177 else if (typedTextCompletion.size() > 0)
01178 typedTextCompletionIdx = typedTextCompletion.size() - 1;
01179 if (typedTextCompletionIdx >= 0) {
01180 string::size_type pos = typedText.rfind('/', typedText.length());
01181 if (pos != string::npos)
01182 typedText = typedText.substr(0, pos + 1) + typedTextCompletion[typedTextCompletionIdx];
01183 else
01184 typedText = typedTextCompletion[typedTextCompletionIdx];
01185 }
01186 }
01187 else if (c == '\033')
01188 {
01189 setTextEnterMode(textEnterMode & ~KbAutoComplete);
01190 }
01191 else if (c == '\n' || c == '\r')
01192 {
01193 if (typedText != "")
01194 {
01195 Selection sel = sim->findObjectFromPath(typedText, true);
01196 if (!sel.empty())
01197 {
01198 addToHistory();
01199 sim->setSelection(sel);
01200 }
01201 typedText = "";
01202 }
01203 setTextEnterMode(textEnterMode & ~KbAutoComplete);
01204 }
01205 return;
01206 }
01207
01208 char C = toupper(c);
01209 switch (C)
01210 {
01211 case '\001':
01212 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowAtmospheres);
01213 notifyWatchers(RenderFlagsChanged);
01214 break;
01215
01216 case '\002':
01217 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowBoundaries);
01218 notifyWatchers(RenderFlagsChanged);
01219 break;
01220
01221 case '\n':
01222 case '\r':
01223 setTextEnterMode(textEnterMode | KbAutoComplete);
01224 break;
01225
01226 case '\b':
01227 sim->setSelection(sim->getSelection().parent());
01228 break;
01229
01230 case '\014':
01231 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowNightMaps);
01232 notifyWatchers(RenderFlagsChanged);
01233 break;
01234
01235 case '\013':
01236 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowMarkers);
01237 if (renderer->getRenderFlags() & Renderer::ShowMarkers)
01238 {
01239 flash(_("Markers enabled"));
01240 }
01241 else
01242 flash(_("Markers disabled"));
01243 notifyWatchers(RenderFlagsChanged);
01244 break;
01245
01246 case '\005':
01247 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowEclipseShadows);
01248 notifyWatchers(RenderFlagsChanged);
01249 break;
01250
01251 case '\007':
01252 flash(_("Goto surface"));
01253 addToHistory();
01254
01255 sim->geosynchronousFollow();
01256 sim->gotoSurface(5.0);
01257
01258 break;
01259
01260 case '\006':
01261 flash(_("Alt-azimuth mode"));
01262 addToHistory();
01263 altAzimuthMode = !altAzimuthMode;
01264 break;
01265
01266 case 127:
01267 deleteView();
01268 break;
01269
01270 case '\011':
01271 activeView++;
01272 if (activeView >= (int) views.size())
01273 activeView = 0;
01274 sim->setActiveObserver(views[activeView]->observer);
01275 if (!showActiveViewFrame)
01276 flashFrameStart = currentTime;
01277 break;
01278
01279 case '\020':
01280 if (!sim->getSelection().empty())
01281 {
01282 Selection sel = sim->getSelection();
01283 if (sim->getUniverse()->isMarked(sel, 1))
01284 {
01285 sim->getUniverse()->unmarkObject(sel, 1);
01286 }
01287 else
01288 {
01289 sim->getUniverse()->markObject(sel,
01290 10.0f,
01291 Color(0.0f, 1.0f, 0.0f, 0.9f),
01292 Marker::Diamond,
01293 1);
01294 }
01295 }
01296 break;
01297
01298 case '\025':
01299 splitView(View::VerticalSplit);
01300 break;
01301
01302 case '\022':
01303 splitView(View::HorizontalSplit);
01304 break;
01305
01306 case '\004':
01307 singleView();
01308 break;
01309
01310 case '\023':
01311 renderer->setStarStyle((Renderer::StarStyle) (((int) renderer->getStarStyle() + 1) %
01312 (int) Renderer::StarStyleCount));
01313 switch (renderer->getStarStyle())
01314 {
01315 case Renderer::FuzzyPointStars:
01316 flash(_("Star style: fuzzy points"));
01317 break;
01318 case Renderer::PointStars:
01319 flash(_("Star style: points"));
01320 break;
01321 case Renderer::ScaledDiscStars:
01322 flash(_("Star style: scaled discs"));
01323 break;
01324 default:
01325 break;
01326 }
01327
01328 notifyWatchers(RenderFlagsChanged);
01329 break;
01330
01331 case '\024':
01332 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowCometTails);
01333 if (renderer->getRenderFlags() & Renderer::ShowCometTails)
01334 {
01335 flash(_("Comet tails enabled"));
01336 }
01337 else
01338 flash(_("Comet tails disabled"));
01339 notifyWatchers(RenderFlagsChanged);
01340 break;
01341
01342 case '\026':
01343 {
01344 GLContext* context = renderer->getGLContext();
01345 GLContext::GLRenderPath path = context->getRenderPath();
01346 GLContext::GLRenderPath newPath = context->nextRenderPath();
01347
01348 if (newPath != path)
01349 {
01350 switch (newPath)
01351 {
01352 case GLContext::GLPath_Basic:
01353 flash(_("Render path: Basic"));
01354 break;
01355 case GLContext::GLPath_Multitexture:
01356 flash(_("Render path: Multitexture"));
01357 break;
01358 case GLContext::GLPath_NvCombiner:
01359 flash(_("Render path: NVIDIA combiners"));
01360 break;
01361 case GLContext::GLPath_DOT3_ARBVP:
01362 flash(_("Render path: OpenGL vertex program"));
01363 break;
01364 case GLContext::GLPath_NvCombiner_NvVP:
01365 flash(_("Render path: NVIDIA vertex program and combiners"));
01366 break;
01367 case GLContext::GLPath_NvCombiner_ARBVP:
01368 flash(_("Render path: OpenGL vertex program/NVIDIA combiners"));
01369 break;
01370 case GLContext::GLPath_ARBFP_ARBVP:
01371 flash(_("Render path: OpenGL 1.5 vertex/fragment program"));
01372 break;
01373 case GLContext::GLPath_NV30:
01374 flash(_("Render path: NVIDIA GeForce FX"));
01375 break;
01376 case GLContext::GLPath_GLSL:
01377 flash(_("Render path: OpenGL 2.0"));
01378 break;
01379 }
01380 context->setRenderPath(newPath);
01381 notifyWatchers(RenderFlagsChanged);
01382 }
01383 }
01384 break;
01385
01386 case '\027':
01387 wireframe = !wireframe;
01388 renderer->setRenderMode(wireframe ? GL_LINE : GL_FILL);
01389 break;
01390
01391 case '\030':
01392 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowSmoothLines);
01393 notifyWatchers(RenderFlagsChanged);
01394 break;
01395
01396 case '\031':
01397 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowAutoMag);
01398 if (renderer->getRenderFlags() & Renderer::ShowAutoMag)
01399 {
01400 flash(_("Auto-magnitude enabled"));
01401 setFaintestAutoMag();
01402 }
01403 else
01404 flash(_("Auto-magnitude disabled"));
01405 notifyWatchers(RenderFlagsChanged);
01406 break;
01407
01408
01409 case '\033':
01410 cancelScript();
01411 addToHistory();
01412 if (textEnterMode != KbNormal)
01413 {
01414 setTextEnterMode(KbNormal);
01415 }
01416 else
01417 {
01418 if (sim->getObserverMode() == Observer::Travelling)
01419 sim->setObserverMode(Observer::Free);
01420 else
01421 sim->setFrame(astro::Universal, Selection());
01422 if (!sim->getTrackedObject().empty())
01423 sim->setTrackedObject(Selection());
01424 }
01425 flash(_("Cancel"));
01426 break;
01427
01428 case ' ':
01429 if (paused)
01430 {
01431 sim->setTimeScale(timeScale);
01432 scriptPaused = false;
01433 paused = false;
01434 }
01435 else
01436 {
01437 sim->setTimeScale(0.0);
01438 paused = true;
01439
01440
01441
01442
01443 #ifdef CELX
01444 if (runningScript != NULL || celxScript != NULL)
01445 #else
01446 if (runningScript != NULL)
01447 #endif
01448 scriptPaused = true;
01449 else
01450 scriptPaused = false;
01451 }
01452
01453 if (paused)
01454 {
01455 if (scriptPaused)
01456 flash(_("Time and script are paused"));
01457 else
01458 flash(_("Time is paused"));
01459 }
01460 else
01461 {
01462 flash(_("Resume"));
01463 }
01464 break;
01465
01466 case '!':
01467 if (editMode)
01468 showSelectionInfo(sim->getSelection());
01469 else
01470 {
01471 double t = (double) time(NULL) / 86400.0 +
01472 (double) astro::Date(1970, 1, 1);
01473 sim->setTime(t);
01474 }
01475 break;
01476
01477 case '%':
01478 {
01479 const ColorTemperatureTable* current =
01480 renderer->getStarColorTable();
01481 if (current == GetStarColorTable(ColorTable_Enhanced))
01482 {
01483 renderer->setStarColorTable(GetStarColorTable(ColorTable_Blackbody_D65));
01484 }
01485 else if (current == GetStarColorTable(ColorTable_Blackbody_D65))
01486 {
01487 renderer->setStarColorTable(GetStarColorTable(ColorTable_Enhanced));
01488 }
01489 else
01490 {
01491
01492 }
01493 }
01494 break;
01495
01496 case '^':
01497 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowNebulae);
01498 notifyWatchers(RenderFlagsChanged);
01499 break;
01500
01501
01502 case '&':
01503 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::LocationLabels);
01504 notifyWatchers(LabelFlagsChanged);
01505 break;
01506
01507 case '*':
01508 addToHistory();
01509 sim->reverseObserverOrientation();
01510 break;
01511
01512 case '?':
01513 addToHistory();
01514 if (!sim->getSelection().empty())
01515 {
01516 Vec3d v = sim->getSelection().getPosition(sim->getTime()) -
01517 sim->getObserver().getPosition();
01518 int hours, mins;
01519 float secs;
01520 char buf[128];
01521 if (astro::microLightYearsToKilometers(v.length()) >=
01522 86400.0 * astro::speedOfLight)
01523 {
01524
01525 sprintf(buf, _("Light travel time: %.4f yr "),
01526 v.length() * 1.0e-6);
01527 flash(buf, 2.0);
01528 }
01529 else
01530 {
01531
01532 getLightTravelDelay(v.length(), hours, mins, secs);
01533 if (hours == 0)
01534 sprintf(buf, _("Light travel time: %d min %.1f s"),
01535 mins, secs);
01536 else
01537 sprintf(buf, _("Light travel time: %d h %d min %.1f s")
01538 ,hours, mins, secs);
01539 flash(buf, 2.0);
01540 }
01541 }
01542 break;
01543
01544 case '-':
01545 addToHistory();
01546
01547 if (sim->getSelection().body() &&
01548 (sim->getTargetSpeed() < 0.99 *
01549 astro::kilometersToMicroLightYears(astro::speedOfLight)))
01550 {
01551 Vec3d v = sim->getSelection().getPosition(sim->getTime()) -
01552 sim->getObserver().getPosition();
01553 lightTravelFlag = !lightTravelFlag;
01554 if (lightTravelFlag)
01555 {
01556 flash(_("Light travel delay included"),2.0);
01557 setLightTravelDelay(v.length());
01558 }
01559 else
01560 {
01561 flash(_("Light travel delay switched off"),2.0);
01562 setLightTravelDelay(-v.length());
01563 }
01564 }
01565 else
01566 {
01567 flash(_("Light travel delay ignored"));
01568 }
01569 break;
01570
01571 case ',':
01572 addToHistory();
01573 if (observer->getFOV() > MinimumFOV)
01574 {
01575 observer->setFOV(observer->getFOV() / 1.05f);
01576 setZoomFromFOV();
01577 if((renderer->getRenderFlags() & Renderer::ShowAutoMag))
01578 {
01579 setFaintestAutoMag();
01580 char buf[128];
01581 sprintf(buf, _("Magnitude limit: %.2f"), sim->getFaintestVisible());
01582 flash(buf);
01583 }
01584 }
01585 break;
01586
01587 case '.':
01588 addToHistory();
01589 if (observer->getFOV() < MaximumFOV)
01590 {
01591 observer->setFOV(observer->getFOV() * 1.05f);
01592 setZoomFromFOV();
01593 if((renderer->getRenderFlags() & Renderer::ShowAutoMag) != 0)
01594 {
01595 setFaintestAutoMag();
01596 char buf[128];
01597 sprintf(buf, _("Magnitude limit: %.2f"), sim->getFaintestVisible());
01598 flash(buf);
01599 }
01600 }
01601 break;
01602
01603 case '+':
01604 addToHistory();
01605 if (observer->getDisplayedSurface() != "")
01606 {
01607 observer->setDisplayedSurface("");
01608 flash(_("Using normal surface textures."));
01609 }
01610 else
01611 {
01612 observer->setDisplayedSurface(_("limit of knowledge"));
01613 flash(_("Using limit of knowledge surface textures."));
01614 }
01615 break;
01616
01617 case '/':
01618 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowDiagrams);
01619 notifyWatchers(RenderFlagsChanged);
01620 break;
01621
01622 case '0':
01623 addToHistory();
01624 sim->selectPlanet(-1);
01625 break;
01626
01627 case '1':
01628 case '2':
01629 case '3':
01630 case '4':
01631 case '5':
01632 case '6':
01633 case '7':
01634 case '8':
01635 case '9':
01636 addToHistory();
01637 sim->selectPlanet(c - '1');
01638 break;
01639
01640 case ';':
01641 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowCelestialSphere);
01642 notifyWatchers(RenderFlagsChanged);
01643 break;
01644
01645 case '=':
01646 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::ConstellationLabels);
01647 notifyWatchers(LabelFlagsChanged);
01648 break;
01649
01650 case 'B':
01651 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::StarLabels);
01652 notifyWatchers(LabelFlagsChanged);
01653 break;
01654
01655 case 'C':
01656 addToHistory();
01657 if (c == 'c')
01658 sim->centerSelection();
01659 else
01660 sim->centerSelectionCO();
01661 break;
01662
01663 case 'D':
01664 addToHistory();
01665 if (config->demoScriptFile != "")
01666 runScript(config->demoScriptFile);
01667 break;
01668
01669 case 'E':
01670 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::GalaxyLabels);
01671 notifyWatchers(LabelFlagsChanged);
01672 break;
01673
01674 case 'F':
01675 addToHistory();
01676 flash(_("Follow"));
01677 sim->follow();
01678 break;
01679
01680 case 'G':
01681 addToHistory();
01682 if (sim->getFrame().coordSys == astro::Universal)
01683 sim->follow();
01684 sim->gotoSelection(5.0, Vec3f(0, 1, 0), astro::ObserverLocal);
01685 break;
01686
01687 case 'H':
01688 addToHistory();
01689 sim->setSelection(sim->getUniverse()->getStarCatalog()->find(0));
01690 break;
01691
01692 case 'I':
01693 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowCloudMaps);
01694 notifyWatchers(RenderFlagsChanged);
01695 break;
01696
01697 case 'J':
01698 addToHistory();
01699 timeScale = -timeScale;
01700 if (!paused)
01701 sim->setTimeScale(timeScale);
01702 if (timeScale >= 0)
01703 flash(_("Time: Forward"));
01704 else
01705 flash(_("Time: Backward"));
01706 break;
01707
01708 case 'K':
01709 addToHistory();
01710 {
01711 timeScale /= timeScaleFactor;
01712 if (!paused)
01713 sim->setTimeScale(timeScale);
01714 char buf[128];
01715 sprintf(buf, _("Time rate: %.1f"), timeScale);
01716 flash(buf);
01717 }
01718 break;
01719
01720 case 'L':
01721 addToHistory();
01722 if (timeScale < MaximumTimeRate)
01723 {
01724 timeScale *= timeScaleFactor;
01725 if (!paused)
01726 sim->setTimeScale(timeScale);
01727 char buf[128];
01728 sprintf(buf, _("Time rate: %.1f"), timeScale);
01729 flash(buf);
01730 }
01731 break;
01732
01733 case 'M':
01734 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::MoonLabels);
01735 notifyWatchers(LabelFlagsChanged);
01736 break;
01737
01738 case 'N':
01739 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::SpacecraftLabels);
01740 notifyWatchers(LabelFlagsChanged);
01741 break;
01742
01743 case 'O':
01744 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowOrbits);
01745 notifyWatchers(RenderFlagsChanged);
01746 break;
01747
01748 case 'P':
01749 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::PlanetLabels);
01750 notifyWatchers(LabelFlagsChanged);
01751 break;
01752
01753 case 'Q':
01754 sim->setTargetSpeed(-sim->getTargetSpeed());
01755 break;
01756
01757 case 'R':
01758 if (c == 'r')
01759 renderer->setResolution(renderer->getResolution() - 1);
01760 else
01761 renderer->setResolution(renderer->getResolution() + 1);
01762 switch (renderer->getResolution())
01763 {
01764 case 0:
01765 flash(_("Low res textures"));
01766 break;
01767 case 1:
01768 flash(_("Medium res textures"));
01769 break;
01770 case 2:
01771 flash(_("High res textures"));
01772 break;
01773 }
01774 break;
01775
01776 case 'S':
01777 sim->setTargetSpeed(0);
01778 break;
01779
01780 case 'T':
01781 addToHistory();
01782 if (sim->getTrackedObject().empty())
01783 sim->setTrackedObject(sim->getSelection());
01784 else
01785 sim->setTrackedObject(Selection());
01786 break;
01787
01788 case 'U':
01789 renderer->setRenderFlags(renderer->getRenderFlags() ^ Renderer::ShowGalaxies);
01790 notifyWatchers(RenderFlagsChanged);
01791 break;
01792
01793 case 'V':
01794 setHudDetail((getHudDetail() + 1) % 3);
01795 break;
01796
01797 case 'W':
01798 if (c == 'w')
01799 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::AsteroidLabels);
01800 else
01801 renderer->setLabelMode(renderer->getLabelMode() ^ Renderer::CometLabels);
01802 notifyWatchers(LabelFlagsChanged);
01803 break;
01804
01805 case 'X':
01806 sim->setTargetSpeed(sim->getTargetSpeed());
01807 break;
01808
01809 case 'Y':
01810 flash(_("Sync Orbit"));
01811 addToHistory();
01812 sim->geosynchronousFollow();
01813 break;
01814
01815 case ':':
01816 flash(_("Lock"));
01817 addToHistory();
01818 sim->phaseLock();
01819 break;
01820
01821 case '"':
01822 flash(_("Chase"));
01823 addToHistory();
01824 sim->chase();
01825 break;
01826
01827
01828
01829 case '[':
01830 if ((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0)
01831 {
01832 if (sim->getFaintestVisible() > 1.0f)
01833 {
01834 setFaintest(sim->getFaintestVisible() - 0.2f);
01835 notifyWatchers(FaintestChanged);
01836 char buf[128];
01837 sprintf(buf, _("Magnitude limit: %.2f"),sim->getFaintestVisible());
01838 flash(buf);
01839 }
01840 }
01841 else if (renderer->getFaintestAM45deg() > 6.0f)
01842 {
01843 renderer->setFaintestAM45deg(renderer->getFaintestAM45deg() - 0.1f);
01844 setFaintestAutoMag();
01845 char buf[128];
01846 sprintf(buf, _("Auto magnitude limit at 45 degrees: %.2f"),renderer->getFaintestAM45deg());
01847 flash(buf);
01848 }
01849 break;
01850
01851 case '\\':
01852 addToHistory();
01853 timeScale = 1.0f;
01854 if (!paused)
01855 sim->setTimeScale(timeScale);
01856 break;
01857
01858 case ']':
01859 if((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0)
01860 {
01861 if (sim->getFaintestVisible() < 15.0f)
01862 {
01863 setFaintest(sim->getFaintestVisible() + 0.2f);
01864 notifyWatchers(FaintestChanged);
01865 char buf[128];
01866 sprintf(buf, _("Magnitude limit: %.2f"),sim->getFaintestVisible());
01867 flash(buf);
01868 }
01869 }
01870 else if (renderer->getFaintestAM45deg() < 12.0f)
01871 {
01872 renderer->setFaintestAM45deg(renderer->getFaintestAM45deg() + 0.1f);
01873 setFaintestAutoMag();
01874 char buf[128];
01875 sprintf(buf, _("Auto magnitude limit at 45 degrees: %.2f"),renderer->getFaintestAM45deg());
01876 flash(buf);
01877 }
01878 break;
01879
01880 case '`':
01881 showFPSCounter = !showFPSCounter;
01882 break;
01883
01884 case '{':
01885 if (renderer->getAmbientLightLevel() > 0.05f)
01886 renderer->setAmbientLightLevel(renderer->getAmbientLightLevel() - 0.05f);
01887 else
01888 renderer->setAmbientLightLevel(0.0f);
01889 notifyWatchers(AmbientLightChanged);
01890 break;
01891
01892 case '}':
01893 if (renderer->getAmbientLightLevel() < 0.95f)
01894 renderer->setAmbientLightLevel(renderer->getAmbientLightLevel() + 0.05f);
01895 else
01896 renderer->setAmbientLightLevel(1.0f);
01897 notifyWatchers(AmbientLightChanged);
01898 break;
01899
01900 case '(':
01901 {
01902 char buf[128];
01903 Galaxy::decreaseLightGain();
01904 sprintf(buf, "%s: %3.2f %%", _("Light gain"), Galaxy::getLightGain() * 100.0f);
01905 flash(buf);
01906 notifyWatchers(GalaxyLightGainChanged);
01907 }
01908 break;
01909
01910 case ')':
01911 {
01912 char buf[128];
01913 Galaxy::increaseLightGain();
01914 sprintf(buf, "%s: %3.2f %%", _("Light gain"), Galaxy::getLightGain() * 100.0f);
01915 flash(buf);
01916 notifyWatchers(GalaxyLightGainChanged);
01917 }
01918 break;
01919
01920 case '~':
01921 showConsole = !showConsole;
01922 break;
01923
01924 case '@':
01925 editMode = !editMode;
01926 break;
01927 }
01928 }
01929
01930
01931 void CelestiaCore::getLightTravelDelay(double distance, int& hours, int& mins,
01932 float& secs)
01933 {
01934
01935 double lt = astro::microLightYearsToKilometers(distance)/
01936 (3600.0 * astro::speedOfLight);
01937 hours = (int) lt;
01938 double mm = (lt - hours) * 60;
01939 mins = (int) mm;
01940 secs = (float) ((mm - mins) * 60);
01941 }
01942
01943 void CelestiaCore::setLightTravelDelay(double distance)
01944 {
01945
01946 double lt = astro::microLightYearsToKilometers(distance)/
01947 (86400.0 * astro::speedOfLight);
01948 sim->setTime(sim->getTime() - lt);
01949 }
01950
01951 void CelestiaCore::start(double t)
01952 {
01953 if (config->initScriptFile != "")
01954 {
01955
01956
01957 string filename = config->initScriptFile;
01958 config->initScriptFile = "";
01959 runScript(filename);
01960 }
01961
01962
01963 sim->setTime(t);
01964 sim->update(0.0);
01965
01966 sysTime = timer->getTime();
01967
01968 if (startURL != "")
01969 goToUrl(startURL);
01970 }
01971
01972 void CelestiaCore::setStartURL(string url)
01973 {
01974 if (!url.substr(0,4).compare("cel:"))
01975 {
01976 startURL = url;
01977 config->initScriptFile = "";
01978 }
01979 else
01980 {
01981 config->initScriptFile = url;
01982 }
01983 }
01984
01985
01986 void CelestiaCore::tick()
01987 {
01988 double lastTime = sysTime;
01989 sysTime = timer->getTime();
01990
01991
01992
01993 double dt = 0.0;
01994 if (movieCapture != NULL && recording)
01995 {
01996 dt = 1.0 / movieCapture->getFrameRate();
01997 }
01998 else
01999 {
02000 dt = sysTime - lastTime;
02001 }
02002
02003
02004 if (scriptPaused)
02005 dt = 0.0;
02006
02007 currentTime += dt;
02008
02009
02010 if (zoomMotion != 0.0f)
02011 {
02012 double span = 0.1;
02013 double fraction;
02014
02015 if (currentTime - zoomTime >= span)
02016 fraction = (zoomTime + span) - (currentTime - dt);
02017 else
02018 fraction = dt / span;
02019
02020
02021 if (currentTime - zoomTime >= span)
02022 zoomMotion = 0.0f;
02023 }
02024
02025
02026 if (dollyMotion != 0.0)
02027 {
02028 double span = 0.1;
02029 double fraction;
02030
02031 if (currentTime - dollyTime >= span)
02032 fraction = (dollyTime + span) - (currentTime - dt);
02033 else
02034 fraction = dt / span;
02035
02036 sim->changeOrbitDistance((float) (dollyMotion * fraction));
02037 if (currentTime - dollyTime >= span)
02038 dollyMotion = 0.0f;
02039 }
02040
02041
02042 if (keysPressed[Key_Home])
02043 sim->changeOrbitDistance((float) (-dt * 2));
02044 if (keysPressed[Key_End])
02045 sim->changeOrbitDistance((float) (dt * 2));
02046
02047
02048 Vec3f av = sim->getObserver().getAngularVelocity();
02049
02050 av = av * (float) exp(-dt * RotationDecay);
02051
02052 float fov = sim->getActiveObserver()->getFOV() / stdFOV;
02053 FrameOfReference frame = sim->getFrame();
02054
02055
02056
02057 if (!showConsole)
02058 {
02059 if (!altAzimuthMode)
02060 {
02061 if (keysPressed[Key_Left])
02062 av += Vec3f(0.0f, 0.0f, (float) (dt * -KeyRotationAccel));
02063 if (keysPressed[Key_Right])
02064 av += Vec3f(0.0f, 0.0f, (float) (dt * KeyRotationAccel));
02065 if (keysPressed[Key_Down])
02066 av += Vec3f((float) (dt * fov * -KeyRotationAccel), 0.0f, 0.0f);
02067 if (keysPressed[Key_Up])
02068 av += Vec3f((float) (dt * fov * KeyRotationAccel), 0.0f, 0.0f);
02069 }
02070 else
02071 {
02072 if (!frame.refObject.empty())
02073 {
02074 Quatf orientation = sim->getObserver().getOrientation();
02075 Vec3d upd = sim->getObserver().getPosition() -
02076 frame.refObject.getPosition(sim->getTime());
02077 upd.normalize();
02078
02079 Vec3f up((float) upd.x, (float) upd.y, (float) upd.z);
02080
02081 Vec3f v = up * (float) (KeyRotationAccel * dt);
02082 v = v * (~orientation).toMatrix3();
02083
02084 if (keysPressed[Key_Left])
02085 av -= v;
02086 if (keysPressed[Key_Right])
02087 av += v;
02088 if (keysPressed[Key_Down])
02089 av += Vec3f((float) (dt * fov * -KeyRotationAccel), 0.0f, 0.0f);
02090 if (keysPressed[Key_Up])
02091 av += Vec3f((float) (dt * fov * KeyRotationAccel), 0.0f, 0.0f);
02092 }
02093 }
02094 }
02095
02096 if (keysPressed[Key_NumPad4])
02097 av += Vec3f(0.0f, (float) (dt * fov * -KeyRotationAccel), 0.0f);
02098 if (keysPressed[Key_NumPad6])
02099 av += Vec3f(0.0f, (float) (dt * fov * KeyRotationAccel), 0.0f);
02100 if (keysPressed[Key_NumPad2])
02101 av += Vec3f((float) (dt * fov * -KeyRotationAccel), 0.0f, 0.0f);
02102 if (keysPressed[Key_NumPad8])
02103 av += Vec3f((float) (dt * fov * KeyRotationAccel), 0.0f, 0.0f);
02104 if (keysPressed[Key_NumPad7] || joyButtonsPressed[JoyButton7])
02105 av += Vec3f(0.0f, 0.0f, (float) (dt * -KeyRotationAccel));
02106 if (keysPressed[Key_NumPad9] || joyButtonsPressed[JoyButton8])
02107 av += Vec3f(0.0f, 0.0f, (float) (dt * KeyRotationAccel));
02108
02109
02110 bool bSetTargetSpeed = false;
02111 if (joystickRotation != Vec3f(0.0f, 0.0f, 0.0f))
02112 {
02113 bSetTargetSpeed = true;
02114
02115 av += (float) (dt * KeyRotationAccel) * joystickRotation;
02116 sim->setTargetSpeed(sim->getTargetSpeed());
02117 }
02118
02119 if (keysPressed[Key_NumPad5])
02120 av = av * (float) exp(-dt * RotationBraking);
02121
02122 sim->getObserver().setAngularVelocity(av);
02123
02124 if (keysPressed[(int)'A'] || joyButtonsPressed[JoyButton2])
02125 {
02126 bSetTargetSpeed = true;
02127
02128 if (sim->getTargetSpeed() == 0.0f)
02129 sim->setTargetSpeed(astro::kilometersToMicroLightYears(0.1f));
02130 else
02131 sim->setTargetSpeed(sim->getTargetSpeed() * (float) exp(dt * 3));
02132 }
02133 if (keysPressed[(int)'Z'] || joyButtonsPressed[JoyButton1])
02134 {
02135 bSetTargetSpeed = true;
02136
02137 sim->setTargetSpeed(sim->getTargetSpeed() / (float) exp(dt * 3));
02138 }
02139 if (!bSetTargetSpeed && av.length() > 0.0f)
02140 {
02141
02142
02143 sim->setTargetSpeed(sim->getTargetSpeed());
02144 }
02145
02146 if (!frame.refObject.empty())
02147 {
02148 Quatf q(1.0f);
02149 float coarseness = ComputeRotationCoarseness(*sim);
02150
02151 if (shiftKeysPressed[Key_Left])
02152 q = q * Quatf::yrotation((float) (dt * -KeyRotationAccel * coarseness));
02153 if (shiftKeysPressed[Key_Right])
02154 q = q * Quatf::yrotation((float) (dt * KeyRotationAccel * coarseness));
02155 if (shiftKeysPressed[Key_Up])
02156 q = q * Quatf::xrotation((float) (dt * -KeyRotationAccel * coarseness));
02157 if (shiftKeysPressed[Key_Down])
02158 q = q * Quatf::xrotation((float) (dt * KeyRotationAccel * coarseness));
02159 sim->orbit(q);
02160 }
02161
02162
02163 if (runningScript != NULL)
02164 {
02165 bool finished = runningScript->tick(dt);
02166 if (finished)
02167 cancelScript();
02168 }
02169
02170 #ifdef CELX
02171 if (celxScript != NULL)
02172 {
02173 bool finished = celxScript->tick(dt);
02174 if (finished)
02175 cancelScript();
02176 }
02177 #endif // CELX
02178
02179 sim->update(dt);
02180 }
02181
02182
02183 void CelestiaCore::draw()
02184 {
02185 if (views.size() == 1)
02186 {
02187
02188
02189
02190
02191
02192
02193
02194 glViewport(0, 0, width, height);
02195 renderer->resize(width, height);
02196 sim->render(*renderer);
02197 }
02198 else
02199 {
02200 glEnable(GL_SCISSOR_TEST);
02201 for (vector<View*>::iterator iter = views.begin();
02202 iter != views.end(); iter++)
02203 {
02204 View* view = *iter;
02205
02206 glScissor((GLint) (view->x * width),
02207 (GLint) (view->y * height),
02208 (GLsizei) (view->width * width),
02209 (GLsizei) (view->height * height));
02210 glViewport((GLint) (view->x * width),
02211 (GLint) (view->y * height),
02212 (GLsizei) (view->width * width),
02213 (GLsizei) (view->height * height));
02214 renderer->resize((int) (view->width * width),
02215 (int) (view->height * height));
02216 sim->render(*renderer, *view->observer);
02217 }
02218 glDisable(GL_SCISSOR_TEST);
02219 glViewport(0, 0, width, height);
02220 }
02221
02222 renderOverlay();
02223 if (showConsole)
02224 {
02225 console.setFont(font);
02226 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
02227 console.begin();
02228 glTranslatef(0.0f, 200.0f, 0.0f);
02229 console.render(ConsolePageRows);
02230 console.end();
02231 }
02232
02233
02234 if (movieCapture != NULL && recording)
02235 movieCapture->captureFrame();
02236
02237
02238 nFrames++;
02239 if (nFrames == 100 || sysTime - fpsCounterStartTime > 10.0)
02240 {
02241 fps = (double) nFrames / (sysTime - fpsCounterStartTime);
02242 nFrames = 0;
02243 fpsCounterStartTime = sysTime;
02244 }
02245
02246 #if 0
02247 GLenum err = glGetError();
02248 if (err != GL_NO_ERROR)
02249 {
02250 cout << _("GL error: ") << gluErrorString(err) << '\n';
02251 }
02252 #endif
02253 }
02254
02255
02256 void CelestiaCore::resize(GLsizei w, GLsizei h)
02257 {
02258 if (h == 0)
02259 h = 1;
02260
02261 glViewport(0, 0, w, h);
02262 if (renderer != NULL)
02263 renderer->resize(w, h);
02264 if (overlay != NULL)
02265 overlay->setWindowSize(w, h);
02266 console.setScale(w, h);
02267 width = w;
02268 height = h;
02269
02270 setFOVFromZoom();
02271 }
02272
02273
02274 void CelestiaCore::splitView(View::Type type, View* av, float splitPos)
02275 {
02276 if (av == NULL)
02277 av = views[activeView];
02278 bool vertical = ( type == View::VerticalSplit );
02279 Observer* o = sim->addObserver();
02280 bool tooSmall = false;
02281
02282 switch (type)
02283 {
02284 case View::HorizontalSplit:
02285 if (av->height < 0.2f) tooSmall = true;
02286 break;
02287 case View::VerticalSplit:
02288 if (av->width < 0.2f) tooSmall = true;
02289 break;
02290 case View::ViewWindow:
02291 return;
02292 break;
02293 }
02294
02295 if (tooSmall)
02296 {
02297 flash(_("View too small to be split"));
02298 return;
02299 }
02300 flash(_("Added view"));
02301
02302
02303
02304
02305 *o = *(sim->getActiveObserver());
02306
02307 float w1, h1, w2, h2;
02308 if (vertical)
02309 {
02310 w1 = av->width * splitPos;
02311 w2 = av->width - w1;
02312 h1 = av->height;
02313 h2 = av->height;
02314 }
02315 else
02316 {
02317 w1 = av->width;
02318 w2 = av->width;
02319 h1 = av->height * splitPos;
02320 h2 = av->height - h1;
02321 }
02322
02323 View* split = new View(type,
02324 0,
02325 av->x,
02326 av->y,
02327 av->width,
02328 av->height);
02329 split->parent = av->parent;
02330 if (av->parent != 0)
02331 {
02332 if (av->parent->child1 == av)
02333 av->parent->child1 = split;
02334 else
02335 av->parent->child2 = split;
02336 }
02337 split->child1 = av;
02338
02339 av->width = w1;
02340 av->height = h1;
02341 av->parent = split;
02342
02343 View* view = new View(View::ViewWindow,
02344 o,
02345 av->x + (vertical ? w1 : 0),
02346 av->y + (vertical ? 0 : h1),
02347 w2, h2);
02348 split->child2 = view;
02349 view->parent = split;
02350 view->zoom = av->zoom;
02351
02352 views.insert(views.end(), view);
02353
02354 setFOVFromZoom();
02355 }
02356
02357 void CelestiaCore::setFOVFromZoom()
02358 {
02359 for (vector<View*>::iterator i = views.begin(); i < views.end(); i++)
02360 if ((*i)->type == View::ViewWindow)
02361 {
02362 double fov = 2 * atan(height * (*i)->height / (screenDpi / 25.4) / 2. / distanceToScreen) / (*i)->zoom;
02363 (*i)->observer->setFOV((float) fov);
02364 }
02365 }
02366
02367 void CelestiaCore::setZoomFromFOV()
02368 {
02369 for (vector<View*>::iterator i = views.begin(); i < views.end(); i++)
02370 if ((*i)->type == View::ViewWindow)
02371 {
02372 (*i)->zoom = (float) (2 * atan(height * (*i)->height / (screenDpi / 25.4) / 2. / distanceToScreen) / (*i)->observer->getFOV());
02373 }
02374 }
02375
02376 void CelestiaCore::singleView(View* av)
02377 {
02378 if (av == NULL)
02379 av = views[activeView];
02380
02381 for (unsigned int i = 0; i < views.size(); i++)
02382 {
02383 if (views[i] != av)
02384 {
02385 sim->removeObserver(views[i]->observer);
02386 delete views[i]->observer;
02387 delete views[i];
02388 }
02389 }
02390
02391 av->x = 0.0f;
02392 av->y = 0.0f;
02393 av->width = 1.0f;
02394 av->height = 1.0f;
02395 av->parent = 0;
02396
02397 views.clear();
02398 views.insert(views.end(), av);
02399 activeView = 0;
02400 sim->setActiveObserver(views[activeView]->observer);
02401 setFOVFromZoom();
02402 }
02403
02404 void CelestiaCore::deleteView(View* v)
02405 {
02406 if (v == NULL)
02407 v = views[activeView];
02408
02409 if (v->parent == 0) return;
02410
02411 for(vector<View*>::iterator i = views.begin(); i < views.end() ; i++)
02412 {
02413 if (*i == v)
02414 {
02415 views.erase(i);
02416 break;
02417 }
02418 }
02419
02420 int sign;
02421 View *sibling;
02422 if (v->parent->child1 == v)
02423 {
02424 sibling = v->parent->child2;
02425 sign = -1;
02426 }
02427 else
02428 {
02429 sibling = v->parent->child1;
02430 sign = 1;
02431 }
02432 sibling->parent = v->parent->parent;
02433 if (v->parent->parent != 0) {
02434 if (v->parent->parent->child1 == v->parent)
02435 v->parent->parent->child1 = sibling;
02436 else
02437 v->parent->parent->child2 = sibling;
02438 }
02439
02440 v->walkTreeResize(sibling, sign);
02441
02442 sim->removeObserver(v->observer);
02443 delete(v->observer);
02444 activeView = 0;
02445 sim->setActiveObserver(views[activeView]->observer);
02446
02447 delete(v->parent);
02448 delete(v);
02449
02450 if (!showActiveViewFrame)
02451 flashFrameStart = currentTime;
02452 setFOVFromZoom();
02453 }
02454
02455 bool CelestiaCore::getFramesVisible() const
02456 {
02457 return showViewFrames;
02458 }
02459
02460 void CelestiaCore::setFramesVisible(bool visible)
02461 {
02462 showViewFrames = visible;
02463 }
02464
02465 bool CelestiaCore::getActiveFrameVisible() const
02466 {
02467 return showActiveViewFrame;
02468 }
02469
02470 void CelestiaCore::setActiveFrameVisible(bool visible)
02471 {
02472 showActiveViewFrame = visible;
02473 }
02474
02475
02476 void CelestiaCore::setContextMenuCallback(ContextMenuFunc callback)
02477 {
02478 contextMenuCallback = callback;
02479 }
02480
02481
02482 Renderer* CelestiaCore::getRenderer() const
02483 {
02484 return renderer;
02485 }
02486
02487 Simulation* CelestiaCore::getSimulation() const
02488 {
02489 return sim;
02490 }
02491
02492 void CelestiaCore::showText(string s,
02493 int horig, int vorig,
02494 int hoff, int voff,
02495 double duration)
02496 {
02497 messageText = s;
02498 messageHOrigin = horig;
02499 messageVOrigin = vorig;
02500 messageHOffset = hoff;
02501 messageVOffset = voff;
02502 messageStart = currentTime;
02503 messageDuration = duration;
02504 }
02505
02506 int CelestiaCore::getTextWidth(string s) const
02507 {
02508 return titleFont->getWidth(s);
02509 }
02510
02511 static FormattedNumber SigDigitNum(double v, int digits)
02512 {
02513 return FormattedNumber(v, digits,
02514 FormattedNumber::GroupThousands |
02515 FormattedNumber::SignificantDigits);
02516 }
02517
02518
02519 static void displayDistance(Overlay& overlay, double distance)
02520 {
02521 const char* units = "";
02522
02523 if (abs(distance) >= astro::parsecsToLightYears(1e+6))
02524 {
02525 units = "Mpc";
02526 distance = astro::lightYearsToParsecs(distance) / 1e+6;
02527 }
02528 else if (abs(distance) >= 0.5 * astro::parsecsToLightYears(1e+3))
02529 {
02530 units = "Kpc";
02531 distance = astro::lightYearsToParsecs(distance) / 1e+3;
02532 }
02533 else if (abs(distance) >= astro::AUtoLightYears(1000.0f))
02534 {
02535 units = "ly";
02536 }
02537 else if (abs(distance) >= astro::kilometersToLightYears(10000000.0))
02538 {
02539 units = "au";
02540 distance = astro::lightYearsToAU(distance);
02541 }
02542 else if (abs(distance) > astro::kilometersToLightYears(1.0f))
02543 {
02544 units = "km";
02545 distance = astro::lightYearsToKilometers(distance);
02546 }
02547 else
02548 {
02549 units = "m";
02550 distance = astro::lightYearsToKilometers(distance) * 1000.0f;
02551 }
02552
02553 overlay << SigDigitNum(distance, 5) << ' ' << units;
02554 }
02555
02556
02557 static void displayDuration(Overlay& overlay, double days)
02558 {
02559 if (days > 1.0)
02560 overlay << days << _(" days");
02561 else if (days > 1.0 / 24.0)
02562 overlay << days * 24.0 << _(" hours");
02563 else if (days > 1.0 / (24.0 * 60.0))
02564 overlay << days * 24.0 * 60.0 << _(" minutes");
02565 else
02566 overlay << days * 24.0 * 60.0 * 60.0 << " seconds";
02567 }
02568
02569
02570 static void displayAngle(Overlay& overlay, double angle)
02571 {
02572 int degrees, minutes;
02573 double seconds;
02574 astro::decimalToDegMinSec(angle, degrees, minutes, seconds);
02575
02576 if (degrees > 0)
02577 {
02578 overlay.printf("%d%s %02d' %.1f\"",
02579 degrees, UTF8_DEGREE_SIGN, minutes, seconds);
02580 }
02581 else if (minutes > 0)
02582 {
02583 overlay.printf("%02d' %.1f\"", minutes, seconds);
02584 }
02585 else
02586 {
02587 overlay.printf("%.1f\"", seconds);
02588 }
02589 }
02590
02591
02592 static void displayApparentDiameter(Overlay& overlay,
02593 double radius,
02594 double distance)
02595 {
02596 if (distance > radius)
02597 {
02598 double arcSize = radToDeg(asin(radius / distance) * 2.0);
02599
02600
02601
02602 if (arcSize < 160.0 && arcSize > 1.0 / 3600.0)
02603 {
02604 overlay << _("Apparent diameter: ");
02605 displayAngle(overlay, arcSize);
02606 overlay << '\n';
02607 }
02608 }
02609 }
02610
02611 static void displayApparentMagnitude(Overlay& overlay,
02612 float absMag,
02613 double distance)
02614 {
02615 float appMag = absMag;
02616 if (distance > 32.6167)
02617 {
02618 appMag = astro::absToAppMag(absMag, (float) distance);
02619 overlay << _("Apparent magnitude: ");
02620 }
02621 else
02622 {
02623 overlay << _("Absolute magnitude: ");
02624 }
02625
02626 overlay.printf("%.1f\n", appMag);
02627 }
02628
02629 static void displayAcronym(Overlay& overlay, char* s)
02630 {
02631 if (strchr(s, ' ') != NULL)
02632 {
02633 int length = strlen(s);
02634 char lastChar = ' ';
02635
02636 for (int i = 0; i < length; i++)
02637 {
02638 if (lastChar == ' ' && s[i] != ' ')
02639 overlay << s[i];
02640 lastChar = s[i];
02641 }
02642 }
02643 else
02644 {
02645 overlay << s;
02646 }
02647 }
02648
02649
02650 static void displayStarInfo(Overlay& overlay,
02651 int detail,
02652 Star& star,
02653 const Universe& universe,
02654 double distance)
02655 {
02656 overlay << _("Distance: ");
02657 displayDistance(overlay, distance);
02658 overlay << '\n';
02659
02660 if (!star.getVisibility())
02661 {
02662 overlay << _("Star system barycenter\n");
02663 return;
02664 }
02665
02666 overlay.printf(_("Abs (app) mag: %.2f (%.2f)\n"),
02667 star.getAbsoluteMagnitude(),
02668 astro::absToAppMag(star.getAbsoluteMagnitude(),
02669 (float) distance));
02670
02671 if (star.getLuminosity() > 1.0e-10f)
02672 overlay << _("Luminosity: ") << SigDigitNum(star.getLuminosity(), 3) << "x Sun\n";
02673 overlay << _("Class: ");
02674 if (star.getSpectralType()[0] == 'Q')
02675 overlay << _("Neutron star");
02676 else if (star.getSpectralType()[0] == 'X')
02677 overlay << _("Black hole");
02678 else
02679 overlay << star.getSpectralType();
02680 overlay << '\n';
02681
02682 displayApparentDiameter(overlay, star.getRadius(),
02683 astro::lightYearsToKilometers(distance));
02684
02685 if (detail > 1)
02686 {
02687 overlay << _("Surface temp: ") << SigDigitNum(star.getTemperature(), 3) << " K\n";
02688 overlay.printf(_("Radius: %.2f Rsun\n"), star.getRadius() / 696000.0f);
02689
02690 overlay << _("Rotation period: ");
02691 float period = star.getRotationElements().period;
02692 displayDuration(overlay, period);
02693 overlay << '\n';
02694
02695 SolarSystem* sys = universe.getSolarSystem(&star);
02696 if (sys != NULL && sys->getPlanets()->getSystemSize() != 0)
02697 overlay << _("Planetary companions present\n");
02698 }
02699 }
02700
02701
02702 static void displayDSOinfo(Overlay& overlay, const DeepSkyObject& dso, double distance)
02703 {
02704 char descBuf[128];
02705
02706 dso.getDescription(descBuf, sizeof(descBuf));
02707 overlay << descBuf << '\n';
02708 if (distance >= 0)
02709 {
02710 overlay << _("Distance: ");
02711 displayDistance(overlay, distance);
02712 }
02713 else
02714 {
02715 overlay << _("Distance from center: ");
02716 displayDistance(overlay, distance + dso.getRadius());
02717 }
02718 overlay << '\n';
02719 overlay << _("Radius: ");
02720 displayDistance(overlay, dso.getRadius());
02721 overlay << '\n';
02722
02723 displayApparentDiameter(overlay, dso.getRadius(), distance);
02724 if (dso.getAbsoluteMagnitude() > DSO_DEFAULT_ABS_MAGNITUDE)
02725 {
02726 displayApparentMagnitude(overlay,
02727 dso.getAbsoluteMagnitude(),
02728 distance);
02729 }
02730 }
02731
02732
02733 static void displayPlanetInfo(Overlay& overlay,
02734 int detail,
02735 Body& body,
02736 double t,
02737 double distance,
02738 Vec3d viewVec)
02739 {
02740 double kmDistance = astro::lightYearsToKilometers(distance);
02741
02742 overlay << _("Distance: ");
02743 distance = astro::kilometersToLightYears(kmDistance - body.getRadius());
02744 displayDistance(overlay, distance);
02745 overlay << '\n';
02746
02747 overlay << _("Radius: ");
02748 distance = astro::kilometersToLightYears(body.getRadius());
02749 displayDistance(overlay, distance);
02750 overlay << '\n';
02751
02752 displayApparentDiameter(overlay, body.getRadius(), kmDistance);
02753
02754 if (detail > 1)
02755 {
02756 overlay << _("Day length: ");
02757 displayDuration(overlay, body.getRotationElements().period);
02758 overlay << '\n';
02759
02760 PlanetarySystem* system = body.getSystem();
02761 if (system != NULL)
02762 {
02763 const Star* sun = system->getStar();
02764 if (sun != NULL)
02765 {
02766 double distFromSun = body.getHeliocentricPosition(t).distanceFromOrigin();
02767 float planetTemp = sun->getTemperature() *
02768 (float) (::pow(1.0 - body.getAlbedo(), 0.25) *
02769 sqrt(sun->getRadius() / (2.0 * distFromSun)));
02770 overlay << setprecision(0);
02771 overlay << _("Temperature: ") << planetTemp << " K\n";
02772 overlay << setprecision(3);
02773 }
02774
02775 #if 0
02776
02777
02778
02779 Point3d bodyPos = body.getHeliocentricPosition(t);
02780 float appMag = body.getApparentMagnitude(*sun,
02781 bodyPos - Point3d(0, 0, 0),
02782 viewVec);
02783 overlay.printf(_("Apparent mag: %.2f\n"), appMag);
02784 #endif
02785 }
02786 }
02787 }
02788
02789
02790 static void displayLocationInfo(Overlay& overlay,
02791 Location& location,
02792 double distance)
02793 {
02794 overlay << _("Distance: ");
02795 displayDistance(overlay, distance);
02796 overlay << '\n';
02797
02798 Body* body = location.getParentBody();
02799 if (body != NULL)
02800 {
02801 Vec3f lonLatAlt = body->cartesianToPlanetocentric(location.getPosition());
02802 char ewHemi = ' ';
02803 char nsHemi = ' ';
02804 float lon = 0.0f;
02805 float lat = 0.0f;
02806
02807
02808
02809 if (body->getName() == "Earth" || body->getName() == "Moon")
02810 {
02811 if (lonLatAlt.y < 0.0f)
02812 nsHemi = 'S';
02813 else if (lonLatAlt.y > 0.0f)
02814 nsHemi = 'N';
02815
02816 if (lonLatAlt.x < 0.0f)
02817 ewHemi = 'W';
02818 else if (lonLatAlt.x > 0.0f)
02819 ewHemi = 'E';
02820
02821 lon = abs(radToDeg(lonLatAlt.x));
02822 lat = abs(radToDeg(lonLatAlt.y));
02823 }
02824 else
02825 {
02826
02827 Quatd q = ~body->getEclipticalToEquatorial(astro::J2000);
02828 bool retrograde = (Vec3d(0.0, 1.0, 0.0) * q.toMatrix3()).y < 0.0;
02829
02830 if ((lonLatAlt.y < 0.0f) ^ retrograde)
02831 nsHemi = 'S';
02832 else if ((lonLatAlt.y > 0.0f) ^ retrograde)
02833 nsHemi = 'N';
02834
02835 if (retrograde)
02836 ewHemi = 'E';
02837 else
02838 ewHemi = 'W';
02839
02840 lon = -radToDeg(lonLatAlt.x);
02841 if (lon < 0.0f)
02842 lon += 360.0f;
02843 lat = abs(radToDeg(lonLatAlt.y));
02844 }
02845
02846 overlay << body->getName(true).c_str() << " ";
02847 overlay.unsetf(ios::fixed);
02848 overlay << setprecision(6);
02849 overlay << lat << nsHemi << ' ' << lon << ewHemi << '\n';
02850 }
02851 }
02852
02853
02854 static void displaySelectionName(Overlay& overlay,
02855 const Selection& sel,
02856 const Universe& univ)
02857 {
02858 switch (sel.getType())
02859 {
02860 case Selection::Type_Body:
02861 overlay << sel.body()->getName(true).c_str();
02862 break;
02863 case Selection::Type_DeepSky:
02864 overlay << univ.getDSOCatalog()->getDSOName(sel.deepsky());
02865 break;
02866 case Selection::Type_Star:
02867
02868 overlay << univ.getStarCatalog()->getStarName(*sel.star());
02869 break;
02870 case Selection::Type_Location:
02871 overlay << sel.location()->getName(true).c_str();
02872 break;
02873 default:
02874 break;
02875 }
02876 }
02877
02878
02879 static void showViewFrame(const View* v, int width, int height)
02880 {
02881 glBegin(GL_LINE_LOOP);
02882 glVertex3f(v->x * width, v->y * height, 0.0f);
02883 glVertex3f(v->x * width, (v->y + v->height) * height - 1, 0.0f);
02884 glVertex3f((v->x + v->width) * width - 1, (v->y + v->height) * height - 1, 0.0f);
02885 glVertex3f((v->x + v->width) * width - 1, v->y * height, 0.0f);
02886 glEnd();
02887 }
02888
02889
02890 void CelestiaCore::renderOverlay()
02891 {
02892 static locale currentLocale;
02893 static locale cLocale;
02894 static bool localeSet = false;
02895
02896 if (!localeSet) {
02897 cLocale = overlay->getloc();
02898 currentLocale = locale(cLocale, "", std::locale::numeric);
02899 localeSet = true;
02900 }
02901
02902 if (font == NULL)
02903 return;
02904
02905 overlay->setFont(font);
02906
02907 int fontHeight = font->getHeight();
02908 int emWidth = font->getWidth("M");
02909
02910 overlay->begin();
02911
02912
02913 if (views.size() > 1)
02914 {
02915
02916 if (showViewFrames || resizeSplit)
02917 {
02918 glLineWidth(1.0f);
02919 glDisable(GL_TEXTURE_2D);
02920 glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
02921 for(vector<View*>::iterator i = views.begin(); i != views.end(); i++)
02922 {
02923 showViewFrame(*i, width, height);
02924 }
02925 }
02926 glLineWidth(1.0f);
02927
02928
02929 View* av = views[activeView];
02930
02931 if (showActiveViewFrame)
02932 {
02933 glLineWidth(2.0f);
02934 glDisable(GL_TEXTURE_2D);
02935 glColor4f(0.5f, 0.5f, 1.0f, 1.0f);
02936 showViewFrame(av, width, height);
02937 glLineWidth(1.0f);
02938 }
02939
02940 if (currentTime < flashFrameStart + 0.5)
02941 {
02942 glLineWidth(8.0f);
02943 glColor4f(0.5f, 0.5f, 1.0f,
02944 (float) (1.0 - (currentTime - flashFrameStart) / 0.5));
02945 showViewFrame(av, width, height);
02946 glLineWidth(1.0f);
02947 }
02948 }
02949
02950 if (hudDetail > 0)
02951 {
02952
02953 glPushMatrix();
02954 glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
02955 glTranslatef((float) (width - (11 + timeZoneName.length() + 3) * emWidth),
02956 (float) (height - fontHeight),
02957 0.0f);
02958 overlay->beginText();
02959
02960 bool time_displayed = false;
02961 double lt = 0.0;
02962
02963 if (sim->getSelection().getType() == Selection::Type_Body &&
02964 (sim->getTargetSpeed() < 0.99 *
02965 astro::kilometersToMicroLightYears(astro::speedOfLight)))
02966 {
02967 if (lightTravelFlag)
02968 {
02969 Vec3d v = sim->getSelection().getPosition(sim->getTime()) -
02970 sim->getObserver().getPosition();
02971
02972 lt = astro::microLightYearsToKilometers(v.length()) / (86400.0 * astro::speedOfLight);
02973 }
02974 }
02975 else
02976 {
02977 lt = 0.0;
02978 }
02979
02980 overlay->imbue(locale::classic());
02981
02982 if (timeZoneBias != 0 &&
02983 sim->getTime() < 2465442 &&
02984 sim->getTime() > 2415733)
02985 {
02986 time_t time = (int) astro::julianDateToSeconds(sim->getTime() - 2440587.5 + lt);
02987 struct tm *localt = localtime(&time);
02988 if (localt != NULL)
02989 {
02990 astro::Date d;
02991 d.year = localt->tm_year + 1900;
02992 d.month = localt->tm_mon + 1;
02993 d.day = localt->tm_mday;
02994 d.hour = localt->tm_hour;
02995 d.minute = localt->tm_min;
02996 d.seconds = (int) localt->tm_sec;
02997 *overlay << d << " ";
02998 displayAcronym(*overlay, tzname[localt->tm_isdst > 0 ? 1 : 0]);
02999 time_displayed = true;
03000 if (lightTravelFlag && lt > 0.0)
03001 {
03002 glColor4f(0.42f, 1.0f, 1.0f, 1.0f);
03003 *overlay << _(" LT");
03004 glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
03005 }
03006 *overlay << '\n';
03007 }
03008 }
03009
03010 if (!time_displayed)
03011 {
03012 *overlay << astro::Date(sim->getTime() + lt);
03013 *overlay << _(" UTC");
03014 if (lightTravelFlag && lt > 0.0)
03015 {
03016 glColor4f(0.42f, 1.0f, 1.0f, 1.0f);
03017 *overlay << _(" LT");
03018 glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
03019 }
03020 *overlay << '\n';
03021 }
03022
03023 setlocale(LC_NUMERIC, "");
03024 overlay->imbue(currentLocale);
03025
03026 {
03027 *overlay << setprecision(0);
03028 if (abs(abs(timeScale) - 1) < 1e-6)
03029 {
03030 if (sign(timeScale) == 1)
03031 *overlay << _("Real time");
03032 else
03033 *overlay << _("-Real time");
03034 }
03035 else if (abs(timeScale) == 0.0f)
03036 {
03037 *overlay << _("Time stopped");
03038 }
03039 else if (abs(timeScale) > 1.0)
03040 {
03041 *overlay << timeScale << UTF8_MULTIPLICATION_SIGN << _(" faster");
03042 }
03043 else
03044 {
03045 *overlay << 1.0 / timeScale << UTF8_MULTIPLICATION_SIGN << _(" slower");
03046 }
03047 *overlay << setprecision(3);
03048
03049 if (paused)
03050 {
03051 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
03052 *overlay << _(" (Paused)");
03053 }
03054 }
03055
03056 overlay->endText();
03057 glPopMatrix();
03058
03059
03060 glPushMatrix();
03061 glTranslatef(0.0f, (float) (fontHeight * 2 + 5), 0.0f);
03062 glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
03063
03064 overlay->beginText();
03065 *overlay << '\n';
03066 if (showFPSCounter)
03067 *overlay << _("FPS: ") << fps;
03068 overlay->setf(ios::fixed);
03069 *overlay << _("\nSpeed: ") << setprecision(3);
03070
03071 double speed = sim->getObserver().getVelocity().length();
03072 if (speed < astro::kilometersToMicroLightYears(1.0f))
03073 *overlay << astro::microLightYearsToKilometers(speed) * 1000.0f << " m/s";
03074 else if (speed < astro::kilometersToMicroLightYears(10000.0f))
03075 *overlay << astro::microLightYearsToKilometers(speed) << " km/s";
03076 else if (speed < astro::kilometersToMicroLightYears((float) astro::speedOfLight * 100.0f))
03077 *overlay << astro::microLightYearsToKilometers(speed) / astro::speedOfLight << 'c';
03078 else if (speed < astro::AUtoMicroLightYears(1000.0f))
03079 *overlay << astro::microLightYearsToAU(speed) << " AU/s";
03080 else
03081 *overlay << speed * 1e-6 << " ly/s";
03082 *overlay << setprecision(3);
03083
03084 overlay->endText();
03085 glPopMatrix();
03086
03087
03088 glPushMatrix();
03089 glTranslatef((float) (width - emWidth * 15),
03090 (float) (fontHeight * 3 + 5), 0.0f);
03091 overlay->beginText();
03092 glColor4f(0.6f, 0.6f, 1.0f, 1);
03093
03094 if (sim->getObserverMode() == Observer::Travelling)
03095 {
03096 *overlay << _("Travelling ");
03097 double timeLeft = sim->getArrivalTime() - sim->getRealTime();
03098 if (timeLeft >= 1)
03099 *overlay << '(' << (int) timeLeft << ')';
03100 *overlay << '\n';
03101 }
03102 else
03103 {
03104 *overlay << '\n';
03105 }
03106
03107 if (!sim->getTrackedObject().empty())
03108 {
03109 *overlay << _("Track ");
03110 displaySelectionName(*overlay, sim->getTrackedObject(),
03111 *sim->getUniverse());
03112 }
03113 *overlay << '\n';
03114
03115 {
03116 FrameOfReference frame = sim->getFrame();
03117
03118 switch (frame.coordSys)
03119 {
03120 case astro::Ecliptical:
03121 *overlay << _("Follow ");
03122 displaySelectionName(*overlay, frame.refObject,
03123 *sim->getUniverse());
03124 break;
03125 case astro::Geographic:
03126 *overlay << _("Sync Orbit ");
03127 displaySelectionName(*overlay, frame.refObject,
03128 *sim->getUniverse());
03129 break;
03130 case astro::PhaseLock:
03131 *overlay << _("Lock ");
03132 displaySelectionName(*overlay, frame.refObject,
03133 *sim->getUniverse());
03134 *overlay << " -> ";
03135 displaySelectionName(*overlay, frame.targetObject,
03136 *sim->getUniverse());
03137 break;
03138
03139 case astro::Chase:
03140 *overlay << _("Chase ");
03141 displaySelectionName(*overlay, frame.refObject,
03142 *sim->getUniverse());
03143 break;
03144
03145 default:
03146 break;
03147 }
03148
03149 *overlay << '\n';
03150 }
03151
03152 glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
03153
03154
03155 float fov = radToDeg(sim->getActiveObserver()->getFOV());
03156 overlay->printf(_("FOV: "));
03157 displayAngle(*overlay, fov);
03158 overlay->printf(" (%.2f%s)\n", views[activeView]->zoom,
03159 UTF8_MULTIPLICATION_SIGN);
03160 overlay->endText();
03161 glPopMatrix();
03162 }
03163
03164
03165 Selection sel = sim->getSelection();
03166 if (!sel.empty() && hudDetail > 0)
03167 {
03168 glPushMatrix();
03169 glColor4f(0.7f, 0.7f, 1.0f, 1.0f);
03170 glTranslatef(0.0f, (float) (height - titleFont->getHeight()), 0.0f);
03171
03172 overlay->beginText();
03173 Vec3d v = sel.getPosition(sim->getTime()) -
03174 sim->getObserver().getPosition();
03175 switch (sel.getType())
03176 {
03177 case Selection::Type_Star:
03178 {
03179 if (sel != lastSelection)
03180 {
03181 lastSelection = sel;
03182 selectionNames = sim->getUniverse()->getStarCatalog()->getStarNameList(*sel.star());
03183 }
03184
03185 overlay->setFont(titleFont);
03186 *overlay << selectionNames;
03187 overlay->setFont(font);
03188 *overlay << '\n';
03189 displayStarInfo(*overlay,
03190 hudDetail,
03191 *(sel.star()),
03192 *(sim->getUniverse()),
03193 v.length() * 1e-6);
03194 }
03195 break;
03196
03197 case Selection::Type_DeepSky:
03198 {
03199 if (sel != lastSelection)
03200 {
03201 lastSelection = sel;
03202 selectionNames = sim->getUniverse()->getDSOCatalog()->getDSONameList(sel.deepsky());
03203 }
03204
03205 overlay->setFont(titleFont);
03206 *overlay << selectionNames;
03207 overlay->setFont(font);
03208 *overlay << '\n';
03209 displayDSOinfo(*overlay, *sel.deepsky(),
03210 v.length() * 1e-6 - sel.deepsky()->getRadius());
03211 }
03212 break;
03213
03214 case Selection::Type_Body:
03215 {
03216 overlay->setFont(titleFont);
03217 *overlay << sel.body()->getName(true).c_str();
03218 overlay->setFont(font);
03219 *overlay << '\n';
03220 displayPlanetInfo(*overlay,
03221 hudDetail,
03222 *(sel.body()),
03223 sim->getTime(),
03224 v.length() * 1e-6,
03225 v * astro::microLightYearsToKilometers(1.0));
03226 }
03227 break;
03228
03229 case Selection::Type_Location:
03230 overlay->setFont(titleFont);
03231 *overlay << sel.location()->getName(true).c_str();
03232 overlay->setFont(font);
03233 *overlay << '\n';
03234 displayLocationInfo(*overlay,
03235 *(sel.location()),
03236 v.length() * 1e-6);
03237 break;
03238
03239 default:
03240 break;
03241 }
03242
03243 overlay->endText();
03244
03245 glPopMatrix();
03246 }
03247
03248
03249 if (textEnterMode & KbAutoComplete)
03250 {
03251 overlay->setFont(titleFont);
03252 glPushMatrix();
03253 glColor4f(0.7f, 0.7f, 1.0f, 0.2f);
03254 overlay->rect(0.0f, 0.0f, (float) width, 100.0f);
03255 glTranslatef(0.0f, fontHeight * 3.0f + 35.0f, 0.0f);
03256 glColor4f(0.6f, 0.6f, 1.0f, 1.0f);
03257 overlay->beginText();
03258 *overlay << _("Target name: ") << ReplaceGreekLetterAbbr(typedText);
03259 overlay->endText();
03260 overlay->setFont(font);
03261 if (typedTextCompletion.size() >= 1)
03262 {
03263 int nb_cols = 4;
03264 int nb_lines = 3;
03265 int start = 0;
03266 glTranslatef(3.0f, -font->getHeight() - 3.0f, 0.0f);
03267 vector<std::string>::const_iterator iter = typedTextCompletion.begin();
03268 if (typedTextCompletionIdx >= nb_cols * nb_lines)
03269 {
03270 start = (typedTextCompletionIdx / nb_lines + 1 - nb_cols) * nb_lines;
03271 iter += start;
03272 }
03273 for (int i=0; iter < typedTextCompletion.end() && i < nb_cols; i++)
03274 {
03275 glPushMatrix();
03276 overlay->beginText();
03277 for (int j = 0; iter < typedTextCompletion.end() && j < nb_lines; iter++, j++)
03278 {
03279 if (i * nb_lines + j == typedTextCompletionIdx - start)
03280 glColor4f(1.0f, 0.6f, 0.6f, 1);
03281 else
03282 glColor4f(0.6f, 0.6f, 1.0f, 1);
03283 *overlay << ReplaceGreekLetterAbbr(*iter) << "\n";
03284 }
03285 overlay->endText();
03286 glPopMatrix();
03287 glTranslatef((float) (width/nb_cols), 0.0f, 0.0f);
03288 }
03289 }
03290 glPopMatrix();
03291 overlay->setFont(font);
03292 }
03293
03294
03295 if (messageText != "" && currentTime < messageStart + messageDuration)
03296 {
03297 int emWidth = titleFont->getWidth("M");
03298 int fontHeight = titleFont->getHeight();
03299 int x = messageHOffset * emWidth;
03300 int y = messageVOffset * fontHeight;
03301
03302 if (messageHOrigin == 0)
03303 x += width / 2;
03304 else if (messageHOrigin > 0)
03305 x += width;
03306 if (messageVOrigin == 0)
03307 y += height / 2;
03308 else if (messageVOrigin > 0)
03309 y += height;
03310 else if (messageVOrigin < 0)
03311 y -= fontHeight;
03312
03313 overlay->setFont(titleFont);
03314 glPushMatrix();
03315
03316 float alpha = 1.0f;
03317 if (currentTime > messageStart + messageDuration - 0.5)
03318 alpha = (float) ((messageStart + messageDuration - currentTime) / 0.5);
03319 glColor4f(1.0f, 1.0f, 1.0f, alpha);
03320 glTranslatef((float) x, (float) y, 0.0f);
03321 overlay->beginText();
03322 *overlay << _(messageText.c_str());
03323 overlay->endText();
03324 glPopMatrix();
03325 overlay->setFont(font);
03326 }
03327
03328 if (movieCapture != NULL)
03329 {
03330 int movieWidth = movieCapture->getWidth();
03331 int movieHeight = movieCapture->getHeight();
03332 glPushMatrix();
03333 glColor4f(1, 0, 0, 1);
03334 overlay->rect((float) ((width - movieWidth) / 2 - 1),
03335 (float) ((height - movieHeight) / 2 - 1),
03336 (float) (movieWidth + 1),
03337 (float) (movieHeight + 1), false);
03338 glTranslatef((float) ((width - movieWidth) / 2),
03339 (float) ((height + movieHeight) / 2 + 2), 0.0f);
03340 *overlay << movieWidth << 'x' << movieHeight << _(" at ") <<
03341 movieCapture->getFrameRate() << _(" fps");
03342 if (recording)
03343 *overlay << _(" Recording");
03344 else
03345 *overlay << _(" Paused");
03346
03347 glPopMatrix();
03348
03349 glPushMatrix();
03350 glTranslatef((float) ((width + movieWidth) / 2 - emWidth * 5),
03351 (float) ((height + movieHeight) / 2 + 2),
03352 0.0f);
03353 float sec = movieCapture->getFrameCount() /
03354 movieCapture->getFrameRate();
03355 int min = (int) (sec / 60);
03356 sec -= min * 60.0f;
03357 overlay->printf("%3d:%05.2f", min, sec);
03358 glPopMatrix();
03359
03360 glPushMatrix();
03361 glTranslatef((float) ((width - movieWidth) / 2),
03362 (float) ((height - movieHeight) / 2 - fontHeight - 2),
03363 0.0f);
03364 *overlay << _("F11 Start/Pause F12 Stop");
03365 glPopMatrix();
03366
03367 glPopMatrix();
03368 }
03369
03370 if (editMode)
03371 {
03372 glPushMatrix();
03373 glTranslatef((float) ((width - font->getWidth(_("Edit Mode"))) / 2),
03374 (float) (height - fontHeight), 0.0f);
03375 glColor4f(1, 0, 1, 1);
03376 *overlay << _("Edit Mode");
03377 glPopMatrix();
03378 }
03379
03380 #if 0
03381
03382 {
03383 glPushMatrix();
03384 glLoadIdentity();
03385 glTranslatef(0.375f, 0.375f, 0);
03386
03387 glEnable(GL_TEXTURE_2D);
03388 for (vector<OverlayImage>::iterator iter = overlayImages.begin();
03389 iter != overlayImages.end(); iter++)
03390 {
03391 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
03392 if (iter->texture != NULL)
03393 {
03394 iter->texture->bind();
03395 glBegin(GL_QUADS);
03396 glTexCoord2f(0, 1);
03397 glVertex2f(iter->left, iter->bottom);
03398 glTexCoord2f(1, 1);
03399 glVertex2f(iter->left + iter->xSize, iter->bottom);
03400 glTexCoord2f(1, 0);
03401 glVertex2f(iter->left + iter->xSize, iter->bottom + iter->ySize);
03402 glTexCoord2f(0, 0);
03403 glVertex2f(iter->left, iter->bottom + iter->ySize);
03404 glEnd();
03405 }
03406 }
03407
03408 glPopMatrix();
03409 }
03410 #endif
03411
03412
03413 if (logoTexture != NULL)
03414 {
03415 glEnable(GL_TEXTURE_2D);
03416 if (currentTime < 5.0)
03417 {
03418 int xSize = (int) (logoTexture->getWidth() * 0.8f);
03419 int ySize = (int) (logoTexture->getHeight() * 0.8f);
03420 int left = (width - xSize) / 2;
03421 int bottom = height / 2;
03422
03423 float topAlpha, botAlpha;
03424 if (currentTime < 4.0)
03425 {
03426 botAlpha = (float) clamp(currentTime / 1.0);
03427 topAlpha = (float) clamp(currentTime / 4.0);
03428 }
03429 else
03430 {
03431 botAlpha = topAlpha = (float) (5.0 - currentTime);
03432 }
03433
03434 logoTexture->bind();
03435 glBegin(GL_QUADS);
03436 glColor4f(0.8f, 0.8f, 1.0f, botAlpha);
03437
03438 glTexCoord2f(0.0f, 1.0f);
03439 glVertex2i(left, bottom);
03440 glTexCoord2f(1.0f, 1.0f);
03441 glVertex2i(left + xSize, bottom);
03442 glColor4f(0.6f, 0.6f, 1.0f, topAlpha);
03443
03444 glTexCoord2f(1.0f, 0.0f);
03445 glVertex2i(left + xSize, bottom + ySize);
03446 glTexCoord2f(0.0f, 0.0f);
03447 glVertex2i(left, bottom + ySize);
03448 glEnd();
03449 }
03450 else
03451 {
03452 delete logoTexture;
03453 logoTexture = NULL;
03454 }
03455 }
03456
03457 overlay->end();
03458 setlocale(LC_NUMERIC, "C");
03459 overlay->imbue(currentLocale);
03460
03461 }
03462
03463
03464 class SolarSystemLoader : public EnumFilesHandler
03465 {
03466 public:
03467 Universe* universe;
03468 ProgressNotifier* notifier;
03469 SolarSystemLoader(Universe* u, ProgressNotifier* pn) : universe(u), notifier(pn) {};
03470
03471 bool process(const string& filename)
03472 {
03473 if (DetermineFileType(filename) == Content_CelestiaCatalog)
03474 {
03475 string fullname = getPath() + '/' + filename;
03476 clog << _("Loading solar system catalog: ") << fullname << '\n';
03477 if (notifier)
03478 notifier->update(filename);
03479
03480 ifstream solarSysFile(fullname.c_str(), ios::in);
03481 if (solarSysFile.good())
03482 {
03483 LoadSolarSystemObjects(solarSysFile,
03484 *universe,
03485 getPath());
03486 }
03487 }
03488
03489 return true;
03490 };
03491 };
03492
03493 template <class OBJDB> class CatalogLoader : public EnumFilesHandler
03494 {
03495 public:
03496 OBJDB* objDB;
03497 string typeDesc;
03498 ContentType contentType;
03499 ProgressNotifier* notifier;
03500
03501 CatalogLoader(OBJDB* db,
03502 const std::string& typeDesc,
03503 const ContentType& contentType,
03504 ProgressNotifier* pn) :
03505 objDB (db),
03506 typeDesc (typeDesc),
03507 contentType(contentType),
03508 notifier(pn)
03509 {
03510 }
03511
03512 bool process(const string& filename)
03513 {
03514 if (DetermineFileType(filename) == contentType)
03515 {
03516 string fullname = getPath() + '/' + filename;
03517 clog << _("Loading ") << typeDesc << " catalog: " << fullname << '\n';
03518 if (notifier)
03519 notifier->update(filename);
03520
03521 ifstream catalogFile(fullname.c_str(), ios::in);
03522 if (catalogFile.good())
03523 {
03524 bool success = objDB->load(catalogFile, getPath());
03525 if (!success)
03526 {
03527 DPRINTF(0, _("Error reading star file: %s\n"),
03528 fullname.c_str());
03529 }
03530 DPRINTF(0, "Error reading %s catalog file: %s\n", typeDesc.c_str(), fullname.c_str());
03531 }
03532 }
03533 return true;
03534 }
03535 };
03536
03537 typedef CatalogLoader<StarDatabase> StarLoader;
03538 typedef CatalogLoader<DSODatabase> DeepSkyLoader;
03539
03540
03541 bool CelestiaCore::initSimulation(const string* configFileName,
03542 const vector<string>* extrasDirs,
03543 ProgressNotifier* progressNotifier)
03544 {
03545
03546
03547 #ifdef REQUIRE_LICENSE_FILE
03548
03549 {
03550 ifstream license("License.txt");
03551 if (!license.good())
03552 {
03553 fatalError(_("License file 'License.txt' is missing!"));
03554 return false;
03555 }
03556 }
03557 #endif
03558
03559 if (configFileName != NULL)
03560 {
03561 config = ReadCelestiaConfig(*configFileName);
03562 }
03563 else
03564 {
03565 config = ReadCelestiaConfig("celestia.cfg");
03566
03567 string localConfigFile = WordExp("~/.celestia.cfg");
03568 if (localConfigFile != "")
03569 ReadCelestiaConfig(localConfigFile.c_str(), config);
03570 }
03571
03572 if (config == NULL)
03573 {
03574 fatalError(_("Error reading configuration file."));
03575 return false;
03576 }
03577
03578
03579
03580
03581 if (extrasDirs != NULL)
03582 {
03583
03584
03585
03586
03587
03588 for (vector<string>::const_iterator iter = extrasDirs->begin();
03589 iter != extrasDirs->end(); iter++)
03590 {
03591 if (find(config->extrasDirs.begin(), config->extrasDirs.end(), *iter) ==
03592 config->extrasDirs.end())
03593 {
03594 config->extrasDirs.push_back(*iter);
03595 }
03596 }
03597 }
03598
03599 KeyRotationAccel = degToRad(config->rotateAcceleration);
03600 MouseRotationSensitivity = degToRad(config->mouseRotationSensitivity);
03601
03602 readFavoritesFile();
03603
03604
03605
03606 if (favorites == NULL)
03607 favorites = new FavoritesList();
03608
03609 universe = new Universe();
03610
03611 if (!readStars(*config, progressNotifier))
03612 {
03613 fatalError(_("Cannot read star database."));
03614 return false;
03615 }
03616
03617
03618
03619 {
03620 SolarSystemCatalog* solarSystemCatalog = new SolarSystemCatalog();
03621 universe->setSolarSystemCatalog(solarSystemCatalog);
03622 for (vector<string>::const_iterator iter = config->solarSystemFiles.begin();
03623 iter != config->solarSystemFiles.end();
03624 iter++)
03625 {
03626 if (progressNotifier)
03627 progressNotifier->update(*iter);
03628
03629 ifstream solarSysFile(iter->c_str(), ios::in);
03630 if (!solarSysFile.good())
03631 {
03632 warning(_("Error opening solar system catalog.\n"));
03633 }
03634 else
03635 {
03636 LoadSolarSystemObjects(solarSysFile, *universe, "");
03637 }
03638 }
03639 }
03640
03641 #if 0
03642
03643 {
03644 bool success = LoadSolarSystemObjectsXML("data/test.xml",
03645 *universe);
03646 if (!success)
03647 warning(_("Error opening test.xml\n"));
03648 }
03649 #endif
03650
03651
03652 {
03653 for (vector<string>::const_iterator iter = config->extrasDirs.begin();
03654 iter != config->extrasDirs.end(); iter++)
03655 {
03656 if (*iter != "")
03657 {
03658 Directory* dir = OpenDirectory(*iter);
03659
03660 SolarSystemLoader loader(universe, progressNotifier);
03661 loader.pushDir(*iter);
03662 dir->enumFiles(loader, true);
03663
03664 delete dir;
03665 }
03666 }
03667 }
03668
03669 DSONameDatabase* dsoNameDB = new DSONameDatabase;
03670 DSODatabase* dsoDB = new DSODatabase;
03671 dsoDB->setNameDatabase(dsoNameDB);
03672
03673
03674 if (config->deepSkyCatalog != "")
03675 {
03676 ifstream dsoFile(config->deepSkyCatalog.c_str(), ios::in);
03677
03678 if (progressNotifier)
03679 progressNotifier->update(config->deepSkyCatalog);
03680
03681 #if 0 //TODO: define a binary file format for DSOs !!
03682 if (!dsoDB->loadBinary(deepSkyFile))
03683 {
03684 cerr << _("Error reading deep sky file\n");
03685 delete dsoDB;
03686 return false;
03687 }
03688 #endif
03689 if (!dsoFile.good())
03690 {
03691 cerr << _("Error opening ") << config->deepSkyCatalog << '\n';
03692 delete dsoDB;
03693 return false;
03694 }
03695 else if (!dsoDB->load(dsoFile, ""))
03696 {
03697 cerr << "Cannot read Deep Sky Objects database." << config->deepSkyCatalog << '\n';
03698 delete dsoDB;
03699 return false;
03700 }
03701 }
03702
03703
03704
03705
03706 {
03707 for (vector<string>::const_iterator iter = config->extrasDirs.begin();
03708 iter != config->extrasDirs.end(); iter++)
03709 {
03710 if (*iter != "")
03711 {
03712 Directory* dir = OpenDirectory(*iter);
03713
03714 DeepSkyLoader loader(dsoDB,
03715 "deep sky object",
03716 Content_CelestiaDeepSkyCatalog,
03717 progressNotifier);
03718 loader.pushDir(*iter);
03719 dir->enumFiles(loader, true);
03720
03721 delete dir;
03722 }
03723 }
03724 }
03725
03726 dsoDB->finish();
03727 universe->setDSOCatalog(dsoDB);
03728
03729
03730 if (config->asterismsFile != "")
03731 {
03732 ifstream asterismsFile(config->asterismsFile.c_str(), ios::in);
03733 if (!asterismsFile.good())
03734 {
03735 warning(_("Error opening asterisms file."));
03736 }
03737 else
03738 {
03739 AsterismList* asterisms = ReadAsterismList(asterismsFile,
03740 *universe->getStarCatalog());
03741 universe->setAsterisms(asterisms);
03742 }
03743 }
03744
03745 if (config->boundariesFile != "")
03746 {
03747 ifstream boundariesFile(config->boundariesFile.c_str(), ios::in);
03748 if (!boundariesFile.good())
03749 {
03750 warning(_("Error opening constellation boundaries files."));
03751 }
03752 else
03753 {
03754 ConstellationBoundaries* boundaries = ReadBoundaries(boundariesFile);
03755 universe->setBoundaries(boundaries);
03756 }
03757 }
03758
03759
03760 if (config->destinationsFile != "")
03761 {
03762 ifstream destfile(config->destinationsFile.c_str());
03763 if (destfile.good())
03764 {
03765 destinations = ReadDestinationList(destfile);
03766 }
03767 }
03768
03769 sim = new Simulation(universe);
03770 if((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0)
03771 sim->setFaintestVisible(config->faintestVisible);
03772
03773 View* view = new View(View::ViewWindow, sim->getActiveObserver(), 0.0f, 0.0f, 1.0f, 1.0f);
03774 views.insert(views.end(), view);
03775
03776 if (!compareIgnoringCase(getConfig()->cursor, "inverting crosshair"))
03777 {
03778 defaultCursorShape = CelestiaCore::InvertedCrossCursor;
03779 }
03780
03781 if (!compareIgnoringCase(getConfig()->cursor, "arrow"))
03782 {
03783 defaultCursorShape = CelestiaCore::ArrowCursor;
03784 }
03785
03786 if (cursorHandler != NULL)
03787 {
03788 cursorHandler->setCursorShape(defaultCursorShape);
03789 }
03790
03791 return true;
03792 }
03793
03794
03795 bool CelestiaCore::initRenderer()
03796 {
03797 renderer->setRenderFlags(Renderer::ShowStars |
03798 Renderer::ShowPlanets |
03799 Renderer::ShowAtmospheres |
03800 Renderer::ShowAutoMag);
03801
03802 GLContext* context = new GLContext();
03803 assert(context != NULL);
03804 if (context == NULL)
03805 return false;
03806
03807 context->init(config->ignoreGLExtensions);
03808
03809 context->setRenderPath(GLContext::GLPath_Basic);
03810 context->setRenderPath(GLContext::GLPath_Multitexture);
03811 context->setRenderPath(GLContext::GLPath_DOT3_ARBVP);
03812 context->setRenderPath(GLContext::GLPath_NvCombiner_NvVP);
03813 context->setRenderPath(GLContext::GLPath_NvCombiner_ARBVP);
03814 cout << _("render path: ") << context->getRenderPath() << '\n';
03815
03816 Renderer::DetailOptions detailOptions;
03817 detailOptions.ringSystemSections = config->ringSystemSections;
03818 detailOptions.orbitPathSamplePoints = config->orbitPathSamplePoints;
03819 detailOptions.shadowTextureSize = config->shadowTextureSize;
03820 detailOptions.eclipseTextureSize = config->eclipseTextureSize;
03821
03822
03823 if (!renderer->init(context, (int) width, (int) height, detailOptions))
03824 {
03825 fatalError(_("Failed to initialize renderer"));
03826 return false;
03827 }
03828
03829
03830 for (vector<string>::const_iterator iter = config->labelledStars.begin();
03831 iter != config->labelledStars.end();
03832 iter++)
03833 {
03834 Star* star = universe->getStarCatalog()->find(*iter);
03835 if (star != NULL)
03836 renderer->addLabelledStar(star, *iter);
03837 }
03838
03839 if((renderer->getRenderFlags() & Renderer::ShowAutoMag) != 0)
03840 {
03841 renderer->setFaintestAM45deg(renderer->getFaintestAM45deg());
03842 setFaintestAutoMag();
03843 }
03844 else
03845 {
03846 renderer->setBrightnessBias(0.1f);
03847 renderer->setSaturationMagnitude(1.0f);
03848 }
03849
03850 if (config->mainFont == "")
03851 font = LoadTextureFont("fonts/default.txf");
03852 else
03853 font = LoadTextureFont(string("fonts/") + config->mainFont);
03854 if (font == NULL)
03855 {
03856 cout << _("Error loading font; text will not be visible.\n");
03857 }
03858 font->buildTexture();
03859
03860 if (config->titleFont != "")
03861 titleFont = LoadTextureFont(string("fonts") + "/" + config->titleFont);
03862 if (titleFont != NULL)
03863 titleFont->buildTexture();
03864 else
03865 titleFont = font;
03866
03867
03868 overlay = new Overlay();
03869 overlay->setWindowSize(width, height);
03870
03871 if (config->labelFont == "")
03872 {
03873 renderer->setFont(font);
03874 }
03875 else
03876 {
03877 TextureFont* labelFont = LoadTextureFont(string("fonts") + "/" + config->labelFont);
03878 if (labelFont == NULL)
03879 {
03880 renderer->setFont(font);
03881 }
03882 else
03883 {
03884 labelFont->buildTexture();
03885 renderer->setFont(labelFont);
03886 }
03887 }
03888
03889 if (config->logoTextureFile != "")
03890 {
03891 logoTexture = LoadTextureFromFile(string("textures") + "/" + config->logoTextureFile);
03892 }
03893
03894 return true;
03895 }
03896
03897
03898 static void loadCrossIndex(StarDatabase* starDB,
03899 StarDatabase::Catalog catalog,
03900 const string& filename)
03901 {
03902 if (!filename.empty())
03903 {
03904 ifstream xrefFile(filename.c_str(), ios::in | ios::binary);
03905 if (xrefFile.good())
03906 {
03907 if (!starDB->loadCrossIndex(catalog, xrefFile))
03908 cerr << _("Error reading cross index ") << filename << '\n';
03909 else
03910 clog << _("Loaded cross index ") << filename << '\n';
03911 }
03912 }
03913 }
03914
03915
03916 bool CelestiaCore::readStars(const CelestiaConfig& cfg,
03917 ProgressNotifier* progressNotifier)
03918 {
03919 StarDetails::InitializeStarTextures();
03920
03921
03922 ifstream starNamesFile(cfg.starNamesFile.c_str(), ios::in);
03923 if (!starNamesFile.good())
03924 {
03925 cerr << _("Error opening ") << cfg.starNamesFile << '\n';
03926 return false;
03927 }
03928
03929 StarNameDatabase* starNameDB = StarNameDatabase::readNames(starNamesFile);
03930 if (starNameDB == NULL)
03931 {
03932 cerr << _("Error reading star names file\n");
03933 return false;
03934 }
03935
03936
03937
03938 StarDatabase* starDB = new StarDatabase();
03939 if (!cfg.starDatabaseFile.empty())
03940 {
03941 if (progressNotifier)
03942 progressNotifier->update(cfg.starDatabaseFile);
03943
03944 ifstream starFile(cfg.starDatabaseFile.c_str(), ios::in | ios::binary);
03945 if (!starFile.good())
03946 {
03947 cerr << _("Error opening ") << cfg.starDatabaseFile << '\n';
03948 delete starDB;
03949 return false;
03950 }
03951
03952 if (!starDB->loadBinary(starFile))
03953 {
03954 delete starDB;
03955 cerr << _("Error reading stars file\n");
03956 return false;
03957 }
03958 }
03959
03960 starDB->setNameDatabase(starNameDB);
03961
03962 loadCrossIndex(starDB, StarDatabase::HenryDraper, cfg.HDCrossIndexFile);
03963 loadCrossIndex(starDB, StarDatabase::SAO, cfg.SAOCrossIndexFile);
03964 loadCrossIndex(starDB, StarDatabase::Gliese, cfg.GlieseCrossIndexFile);
03965
03966
03967
03968 if (!cfg.starCatalogFiles.empty())
03969 {
03970 for (vector<string>::const_iterator iter = config->starCatalogFiles.begin();
03971 iter != config->starCatalogFiles.end(); iter++)
03972 {
03973 if (*iter != "")
03974 {
03975 ifstream starFile(iter->c_str(), ios::in);
03976 if (starFile.good())
03977 {
03978 starDB->load(starFile, "");
03979 }
03980 else
03981 {
03982 cerr << _("Error opening star catalog ") << *iter << '\n';
03983 }
03984 }
03985 }
03986 }
03987
03988
03989 for (vector<string>::const_iterator iter = config->extrasDirs.begin();
03990 iter != config->extrasDirs.end(); iter++)
03991 {
03992 if (*iter != "")
03993 {
03994 Directory* dir = OpenDirectory(*iter);
03995
03996 StarLoader loader(starDB, "star", Content_CelestiaStarCatalog, progressNotifier);
03997 loader.pushDir(*iter);
03998 dir->enumFiles(loader, true);
03999
04000 delete dir;
04001 }
04002 }
04003
04004 starDB->finish();
04005
04006 universe->setStarCatalog(starDB);
04007
04008 return true;
04009 }
04010
04011
04014 void CelestiaCore::setFaintest(float magnitude)
04015 {
04016 renderer->setBrightnessBias(0.1f);
04017 renderer->setSaturationMagnitude(1.0f);
04018 sim->setFaintestVisible(magnitude);
04019 }
04020
04024 void CelestiaCore::setFaintestAutoMag()
04025 {
04026 float faintestMag;
04027 renderer->setBrightnessBias(0.1f);
04028 renderer->autoMag(faintestMag);
04029 sim->setFaintestVisible(faintestMag);
04030 }
04031
04032 void CelestiaCore::fatalError(const string& msg)
04033 {
04034 if (alerter == NULL)
04035 cout << msg;
04036 else
04037 alerter->fatalError(msg);
04038 }
04039
04040
04041 void CelestiaCore::setAlerter(Alerter* a)
04042 {
04043 alerter = a;
04044 }
04045
04046 CelestiaCore::Alerter* CelestiaCore::getAlerter() const
04047 {
04048 return alerter;
04049 }
04050
04054 void CelestiaCore::setCursorHandler(CursorHandler* handler)
04055 {
04056 cursorHandler = handler;
04057 }
04058
04059 CelestiaCore::CursorHandler* CelestiaCore::getCursorHandler() const
04060 {
04061 return cursorHandler;
04062 }
04063
04064 int CelestiaCore::getTimeZoneBias() const
04065 {
04066 return timeZoneBias;
04067 }
04068
04069 bool CelestiaCore::getLightDelayActive() const
04070 {
04071 return lightTravelFlag;
04072 }
04073
04074 void CelestiaCore::setLightDelayActive(bool lightDelayActive)
04075 {
04076 lightTravelFlag = lightDelayActive;
04077 }
04078
04079 void CelestiaCore::setTextEnterMode(int mode)
04080 {
04081 if (mode != textEnterMode)
04082 {
04083 if ((mode & KbAutoComplete) != (textEnterMode & KbAutoComplete))
04084 {
04085 typedText = "";
04086 typedTextCompletion.clear();
04087 typedTextCompletionIdx = -1;
04088 }
04089 textEnterMode = mode;
04090 notifyWatchers(TextEnterModeChanged);
04091 }
04092 }
04093
04094 int CelestiaCore::getTextEnterMode() const
04095 {
04096 return textEnterMode;
04097 }
04098
04099 void CelestiaCore::setScreenDpi(int dpi)
04100 {
04101 screenDpi = dpi;
04102 setFOVFromZoom();
04103 renderer->setScreenDpi(dpi);
04104 }
04105
04106 int CelestiaCore::getScreenDpi() const
04107 {
04108 return screenDpi;
04109 }
04110
04111 void CelestiaCore::setDistanceToScreen(int dts)
04112 {
04113 distanceToScreen = dts;
04114 setFOVFromZoom();
04115 }
04116
04117 int CelestiaCore::getDistanceToScreen() const
04118 {
04119 return distanceToScreen;
04120 }
04121
04122 void CelestiaCore::setTimeZoneBias(int bias)
04123 {
04124 timeZoneBias = bias;
04125 notifyWatchers(TimeZoneChanged);
04126 }
04127
04128
04129 string CelestiaCore::getTimeZoneName() const
04130 {
04131 return timeZoneName;
04132 }
04133
04134
04135 void CelestiaCore::setTimeZoneName(const string& zone)
04136 {
04137 timeZoneName = zone;
04138 }
04139
04140
04141 int CelestiaCore::getHudDetail()
04142 {
04143 return hudDetail;
04144 }
04145
04146 void CelestiaCore::setHudDetail(int newHudDetail)
04147 {
04148 hudDetail = newHudDetail%3;
04149 notifyWatchers(VerbosityLevelChanged);
04150 }
04151
04152
04153 void CelestiaCore::initMovieCapture(MovieCapture* mc)
04154 {
04155 if (movieCapture == NULL)
04156 movieCapture = mc;
04157 }
04158
04159 void CelestiaCore::recordBegin()
04160 {
04161 if (movieCapture != NULL)
04162 recording = true;
04163 }
04164
04165 void CelestiaCore::recordPause()
04166 {
04167 recording = false;
04168 }
04169
04170 void CelestiaCore::recordEnd()
04171 {
04172 if (movieCapture != NULL)
04173 {
04174 recordPause();
04175 movieCapture->end();
04176 delete movieCapture;
04177 movieCapture = NULL;
04178 }
04179 }
04180
04181 bool CelestiaCore::isCaptureActive()
04182 {
04183 return movieCapture != NULL;
04184 }
04185
04186 bool CelestiaCore::isRecording()
04187 {
04188 return recording;
04189 }
04190
04191 void CelestiaCore::flash(const string& s, double duration)
04192 {
04193 if (hudDetail > 0)
04194 showText(s, -1, -1, 0, 5, duration);
04195 }
04196
04197
04198 CelestiaConfig* CelestiaCore::getConfig() const
04199 {
04200 return config;
04201 }
04202
04203
04204 void CelestiaCore::addWatcher(CelestiaWatcher* watcher)
04205 {
04206 assert(watcher != NULL);
04207 watchers.insert(watchers.end(), watcher);
04208 }
04209
04210 void CelestiaCore::removeWatcher(CelestiaWatcher* watcher)
04211 {
04212 vector<CelestiaWatcher*>::iterator iter =
04213 find(watchers.begin(), watchers.end(), watcher);
04214 if (iter != watchers.end())
04215 watchers.erase(iter);
04216 }
04217
04218 void CelestiaCore::notifyWatchers(int property)
04219 {
04220 for (vector<CelestiaWatcher*>::iterator iter = watchers.begin();
04221 iter != watchers.end(); iter++)
04222 {
04223 (*iter)->notifyChange(this, property);
04224 }
04225 }
04226
04227
04228 void CelestiaCore::goToUrl(const string& urlStr)
04229 {
04230 Url url(urlStr, this);
04231 url.goTo();
04232 timeScale = sim->getTimeScale();
04233 notifyWatchers(RenderFlagsChanged|LabelFlagsChanged);
04234 }
04235
04236
04237 void CelestiaCore::addToHistory()
04238 {
04239 Url url(this);
04240 if (!history.empty() && historyCurrent < history.size() - 1)
04241 {
04242
04243 while (historyCurrent != history.size() - 1)
04244 {
04245 history.pop_back();
04246 }
04247 }
04248 history.push_back(url);
04249 historyCurrent = history.size() - 1;
04250 notifyWatchers(HistoryChanged);
04251 }
04252
04253
04254 void CelestiaCore::back()
04255 {
04256 if (historyCurrent == 0) return;
04257 if (historyCurrent == history.size() - 1)
04258 {
04259 addToHistory();
04260 historyCurrent = history.size()-1;
04261 }
04262 historyCurrent--;
04263 history[historyCurrent].goTo();
04264 timeScale = sim->getTimeScale();
04265 notifyWatchers(HistoryChanged|RenderFlagsChanged|LabelFlagsChanged);
04266 }
04267
04268
04269 void CelestiaCore::forward()
04270 {
04271 if (historyCurrent == history.size()-1) return;
04272 historyCurrent++;
04273 history[historyCurrent].goTo();
04274 timeScale = sim->getTimeScale();
04275 notifyWatchers(HistoryChanged|RenderFlagsChanged|LabelFlagsChanged);
04276 }
04277
04278
04279 const vector<Url>& CelestiaCore::getHistory() const
04280 {
04281 return history;
04282 }
04283
04284 vector<Url>::size_type CelestiaCore::getHistoryCurrent() const
04285 {
04286 return historyCurrent;
04287 }
04288
04289 void CelestiaCore::setHistoryCurrent(vector<Url>::size_type curr)
04290 {
04291 if (curr >= history.size()) return;
04292 if (historyCurrent == history.size()) {
04293 addToHistory();
04294 }
04295 historyCurrent = curr;
04296 history[curr].goTo();
04297 notifyWatchers(HistoryChanged|RenderFlagsChanged|LabelFlagsChanged);
04298 }