554 lines
15 KiB
C++
554 lines
15 KiB
C++
// glutmain.cpp
|
|
//
|
|
// Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
|
|
//
|
|
// GLUT front-end for Celestia.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <config.h>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <cctype>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <unistd.h>
|
|
#include <GL/glew.h>
|
|
#ifndef MACOSX
|
|
#include <GL/glut.h>
|
|
#else
|
|
#include <Carbon/Carbon.h>
|
|
#include <GLUT/glut.h>
|
|
#endif
|
|
#include <fmt/printf.h>
|
|
#include <celutil/util.h>
|
|
#include <celutil/debug.h>
|
|
#include <celmath/mathlib.h>
|
|
#include <celengine/astro.h>
|
|
#include <celestia/celestiacore.h>
|
|
/* what are you supposed to be?
|
|
#include "popt.h"
|
|
*/
|
|
|
|
using namespace std;
|
|
|
|
|
|
char AppName[] = "Celestia";
|
|
|
|
static CelestiaCore* appCore = nullptr;
|
|
|
|
//static bool fullscreen = false;
|
|
static bool ready = false;
|
|
|
|
|
|
// Mouse motion tracking
|
|
static int lastX = 0;
|
|
static int lastY = 0;
|
|
static bool leftButton = false;
|
|
static bool middleButton = false;
|
|
static bool rightButton = false;
|
|
|
|
static int mainWindow = 1;
|
|
|
|
// Mouse wheel button assignments
|
|
#define MOUSE_WHEEL_UP 3
|
|
#define MOUSE_WHEEL_DOWN 4
|
|
|
|
|
|
|
|
static void Resize(int w, int h)
|
|
{
|
|
appCore->resize(w, h);
|
|
}
|
|
|
|
|
|
/*
|
|
* Definition of GLUT callback functions
|
|
*/
|
|
|
|
static void Display()
|
|
{
|
|
if (ready)
|
|
{
|
|
appCore->draw();
|
|
glutSwapBuffers();
|
|
}
|
|
}
|
|
|
|
static void Idle()
|
|
{
|
|
if (glutGetWindow() != mainWindow)
|
|
glutSetWindow(mainWindow);
|
|
|
|
appCore->tick();
|
|
|
|
Display();
|
|
}
|
|
|
|
static void MouseDrag(int x, int y)
|
|
{
|
|
int buttons = 0;
|
|
if (leftButton)
|
|
buttons |= CelestiaCore::LeftButton;
|
|
if (rightButton)
|
|
buttons |= CelestiaCore::RightButton;
|
|
if (middleButton)
|
|
buttons |= CelestiaCore::MiddleButton;
|
|
|
|
appCore->mouseMove(x - lastX, y - lastY, buttons);
|
|
|
|
lastX = x;
|
|
lastY = y;
|
|
}
|
|
|
|
static void MouseButton(int button, int state, int x, int y)
|
|
{
|
|
#ifdef MACOSX
|
|
if (button == GLUT_LEFT_BUTTON) {
|
|
UInt32 mods=GetCurrentKeyModifiers ();
|
|
if (mods & optionKey) button=GLUT_MIDDLE_BUTTON;
|
|
else if (mods & cmdKey) button=GLUT_RIGHT_BUTTON;
|
|
}
|
|
#endif /* MACOSX */
|
|
// On Linux, mouse wheel up and down are usually translated into
|
|
// mouse button 4 and 5 down events.
|
|
if (button == MOUSE_WHEEL_UP)
|
|
{
|
|
appCore->mouseWheel(-1.0f, 0);
|
|
}
|
|
else if (button == MOUSE_WHEEL_DOWN)
|
|
{
|
|
appCore->mouseWheel(1.0f, 0);
|
|
}
|
|
else if (button == GLUT_LEFT_BUTTON)
|
|
{
|
|
leftButton = (state == GLUT_DOWN);
|
|
if (state == GLUT_DOWN)
|
|
appCore->mouseButtonDown(x, y, CelestiaCore::LeftButton);
|
|
else
|
|
appCore->mouseButtonUp(x, y, CelestiaCore::LeftButton);
|
|
}
|
|
else if (button == GLUT_RIGHT_BUTTON)
|
|
{
|
|
rightButton = (state == GLUT_DOWN);
|
|
if (state == GLUT_DOWN)
|
|
appCore->mouseButtonDown(x, y, CelestiaCore::RightButton);
|
|
else
|
|
appCore->mouseButtonUp(x, y, CelestiaCore::RightButton);
|
|
}
|
|
else if (button == GLUT_MIDDLE_BUTTON)
|
|
{
|
|
middleButton = (state == GLUT_DOWN);
|
|
if (state == GLUT_DOWN)
|
|
appCore->mouseButtonDown(x, y, CelestiaCore::MiddleButton);
|
|
else
|
|
appCore->mouseButtonUp(x, y, CelestiaCore::MiddleButton);
|
|
}
|
|
|
|
lastX = x;
|
|
lastY = y;
|
|
}
|
|
|
|
static void KeyPress(unsigned char c, int /*x*/, int /*y*/)
|
|
{
|
|
// Ctrl-Q exits
|
|
if (c == '\021')
|
|
exit(0);
|
|
|
|
appCore->charEntered((char) c);
|
|
//appCore->keyDown((int) c);
|
|
}
|
|
|
|
|
|
static void KeyUp(unsigned char c, int /*x*/, int /*y*/)
|
|
{
|
|
appCore->keyUp((int) c);
|
|
}
|
|
|
|
|
|
static void HandleSpecialKey(int key, bool down)
|
|
{
|
|
int k = -1;
|
|
switch (key)
|
|
{
|
|
case GLUT_KEY_UP:
|
|
k = CelestiaCore::Key_Up;
|
|
break;
|
|
case GLUT_KEY_DOWN:
|
|
k = CelestiaCore::Key_Down;
|
|
break;
|
|
case GLUT_KEY_LEFT:
|
|
k = CelestiaCore::Key_Left;
|
|
break;
|
|
case GLUT_KEY_RIGHT:
|
|
k = CelestiaCore::Key_Right;
|
|
break;
|
|
case GLUT_KEY_HOME:
|
|
k = CelestiaCore::Key_Home;
|
|
break;
|
|
case GLUT_KEY_END:
|
|
k = CelestiaCore::Key_End;
|
|
break;
|
|
case GLUT_KEY_F1:
|
|
k = CelestiaCore::Key_F1;
|
|
break;
|
|
case GLUT_KEY_F2:
|
|
k = CelestiaCore::Key_F2;
|
|
break;
|
|
case GLUT_KEY_F3:
|
|
k = CelestiaCore::Key_F3;
|
|
break;
|
|
case GLUT_KEY_F4:
|
|
k = CelestiaCore::Key_F4;
|
|
break;
|
|
case GLUT_KEY_F5:
|
|
k = CelestiaCore::Key_F5;
|
|
break;
|
|
case GLUT_KEY_F6:
|
|
k = CelestiaCore::Key_F6;
|
|
break;
|
|
case GLUT_KEY_F7:
|
|
k = CelestiaCore::Key_F7;
|
|
break;
|
|
case GLUT_KEY_F11:
|
|
k = CelestiaCore::Key_F11;
|
|
break;
|
|
case GLUT_KEY_F12:
|
|
k = CelestiaCore::Key_F12;
|
|
break;
|
|
}
|
|
/* Glut doesn't seem to handle Keypad Keys, so we can't pass them on.
|
|
They will be passed as the appropriate Special keys instead */
|
|
|
|
if (k >= 0)
|
|
{
|
|
if (down)
|
|
appCore->keyDown(k);
|
|
else
|
|
appCore->keyUp(k);
|
|
}
|
|
}
|
|
|
|
|
|
static void SpecialKeyPress(int key, int /*x*/, int /*y*/)
|
|
{
|
|
HandleSpecialKey(key, true);
|
|
}
|
|
|
|
|
|
static void SpecialKeyUp(int key, int /*x*/, int /*y*/)
|
|
{
|
|
HandleSpecialKey(key, false);
|
|
}
|
|
|
|
#ifdef MACOSX
|
|
static void menuCallback (int which);
|
|
static void initMenus(void) {
|
|
int gMain,gNavigation,gTime,gLabels,gRendering;
|
|
int gViews,gSpaceflight,gNumber,gJoystick,gMouse;
|
|
gMain=gNavigation=gTime=gLabels=gRendering=0;
|
|
gViews=gSpaceflight=gNumber=gJoystick=gMouse=0;
|
|
gNavigation=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Center C",101);
|
|
glutAddMenuEntry("Go closer G",102);
|
|
glutAddMenuEntry("Follow F",103);
|
|
glutAddMenuEntry("Orbit Y",104);
|
|
glutAddMenuEntry("Track T",105);
|
|
glutAddMenuEntry("Move closer HOME",106);
|
|
glutAddMenuEntry("Move farther END",107);
|
|
glutAddMenuEntry("Cancel motion ESC",108);
|
|
glutAddMenuEntry("*Roll Camera <- ->",000);
|
|
glutAddMenuEntry("*Camera Pitch UP DOWN",000);
|
|
gTime=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("10x faster L",201);
|
|
glutAddMenuEntry("10x slower K",202);
|
|
glutAddMenuEntry("Reverse time J",203);
|
|
gLabels=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Toggle planet/moon N",301);
|
|
glutAddMenuEntry("Toggle star B",302);
|
|
glutAddMenuEntry("Toggle constellation =",303);
|
|
glutAddMenuEntry("Toggle info text V",304);
|
|
gRendering=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Wireframe W",401);
|
|
glutAddMenuEntry("Per-pixel lighting CTRL+P",402);
|
|
glutAddMenuEntry("Vertex programs CTRL+V",403);
|
|
glutAddMenuEntry("Show FPS `",404);
|
|
glutAddMenuEntry("*Limiting magnitude ] [",000);
|
|
glutAddMenuEntry("*Ambient illumination } {",000);
|
|
glutAddMenuEntry("*Narrow/Widen FOV , .",000);
|
|
gViews=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Galaxies U",501);
|
|
glutAddMenuEntry("Planet orbits O",502);
|
|
glutAddMenuEntry("Constellations /",503);
|
|
glutAddMenuEntry("Atmospheres CTRL+A",504);
|
|
glutAddMenuEntry("Cloud textures I",505);
|
|
glutAddMenuEntry("Night side planet maps CTRL+L",506);
|
|
glutAddMenuEntry("Equatorial coordinates ;",507);
|
|
gSpaceflight=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Stop F1",601);
|
|
glutAddMenuEntry("Set velocity to 1 km/s F2",602);
|
|
glutAddMenuEntry("Set velocity to 1,000 km/s F3",603);
|
|
glutAddMenuEntry("Set velocity to lightspeed F4",604);
|
|
glutAddMenuEntry("Set velocity to 10^6 km/s F5",605);
|
|
glutAddMenuEntry("Set velocity to 1 AU/s F6",606);
|
|
glutAddMenuEntry("Set velocity to 1 ly/s F7",607);
|
|
glutAddMenuEntry("Increase velocity (exp) A",608);
|
|
glutAddMenuEntry("Decrease velocity (exp) Z",609);
|
|
glutAddMenuEntry("Reverse direction Q",610);
|
|
glutAddMenuEntry("Movement to screen origin X",611);
|
|
gNumber=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Stop rotation 5",701);
|
|
glutAddMenuEntry("*Yaw left/right 4 6",702);
|
|
glutAddMenuEntry("*Pitch up/down 2 8",703);
|
|
glutAddMenuEntry("*Roll left/right 7 9",704);
|
|
gJoystick=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Enable joystick F8",801);
|
|
glutAddMenuEntry("*Yaw X axis",000);
|
|
glutAddMenuEntry("*Pitch Y axis",000);
|
|
glutAddMenuEntry("*Roll L,R trigger",000);
|
|
glutAddMenuEntry("*Speed Button 1,2",000);
|
|
gMain=glutCreateMenu(menuCallback);
|
|
glutAddMenuEntry("Select the sun (Home) H",001);
|
|
/* glutAddMenuEntry("Select by name ENTER",002); */
|
|
glutAddMenuEntry("Run demo D",003);
|
|
/*
|
|
gMouse=glutCreateMenu(menuCallback);
|
|
glutAddSubMenu("*Orient camera CLICK+DRAG",000);
|
|
glutAddSubMenu("*Orbit object RCLICK+DRAG",000);
|
|
*/
|
|
glutAddSubMenu("Selected Object", gNavigation);
|
|
glutAddSubMenu("Time",gTime);
|
|
glutAddSubMenu("Labels",gLabels);
|
|
glutAddSubMenu("Rendering",gRendering);
|
|
glutAddSubMenu("Views",gViews);
|
|
glutAddSubMenu("Spaceflight",gSpaceflight);
|
|
glutAddSubMenu("Number Pad",gNumber);
|
|
glutAddSubMenu("Joystick",gJoystick);
|
|
/*
|
|
glutAddSubMenu("Mouse",gMouse);
|
|
*/
|
|
glutAttachMenu(GLUT_RIGHT_BUTTON);
|
|
}
|
|
|
|
#define CTRLKEY(KEY) (KEY-'a'+(char)1)
|
|
#define caseKey(CASE,WHICH) \
|
|
case (CASE): \
|
|
appCore->charEntered((char)(WHICH)); \
|
|
appCore->keyDown((int)(WHICH)); \
|
|
appCore->keyUp((int)(WHICH)); \
|
|
break
|
|
#define caseKeySpecial(CASE,WHICH) \
|
|
case (CASE): \
|
|
appCore->keyDown(CelestiaCore::Key_##WHICH); \
|
|
appCore->keyUp(CelestiaCore::Key_##WHICH); \
|
|
break
|
|
|
|
static void menuCallback (int which) {
|
|
switch(which) {
|
|
// main menu
|
|
caseKey(1,'h');
|
|
caseKey(2,0x13);
|
|
caseKey(3,'d');
|
|
// navigation
|
|
caseKey(101,'c');
|
|
caseKey(102,'g');
|
|
caseKey(103,'f');
|
|
caseKey(104,'y');
|
|
caseKey(105,'t');
|
|
caseKeySpecial(106,Home);
|
|
caseKeySpecial(107,End);
|
|
caseKey(108,0x27);
|
|
// time
|
|
caseKey(201,'l');
|
|
caseKey(202,'k');
|
|
caseKey(203,'j');
|
|
// labels
|
|
caseKey(301,'n');
|
|
caseKey(302,'b');
|
|
caseKey(303,'=');
|
|
caseKey(304,'v');
|
|
// rendering
|
|
caseKey(401,'w');
|
|
caseKey(402,CTRLKEY('p'));
|
|
caseKey(403,CTRLKEY('v'));
|
|
caseKey(404,'`');
|
|
// views
|
|
caseKey(501,'u');
|
|
caseKey(502,'o');
|
|
caseKey(503,'/');
|
|
caseKey(504,CTRLKEY('a'));
|
|
caseKey(505,'i');
|
|
caseKey(506,CTRLKEY('l'));
|
|
caseKey(507,';');
|
|
// spaceflight
|
|
caseKeySpecial(601,F1);
|
|
caseKeySpecial(602,F2);
|
|
caseKeySpecial(603,F3);
|
|
caseKeySpecial(604,F4);
|
|
caseKeySpecial(605,F5);
|
|
caseKeySpecial(606,F6);
|
|
caseKeySpecial(607,F7);
|
|
caseKey(608,'a');
|
|
caseKey(609,'z');
|
|
caseKey(610,'q');
|
|
caseKey(611,'x');
|
|
// number pad
|
|
caseKey(701,5);
|
|
// joystick
|
|
caseKeySpecial(801,F8);
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
#ifdef MACOSX
|
|
static void killLastSlash(char *buf) {
|
|
int i=strlen(buf);
|
|
while (--i && buf[i]!='/') {}
|
|
if (buf[i]=='/') buf[i]=0;
|
|
}
|
|
static void dirFixup(char *argv0) {
|
|
char *myPath;
|
|
assert(myPath=(char *)malloc(strlen(argv0)+128));
|
|
strcpy(myPath,argv0);
|
|
killLastSlash(myPath);
|
|
killLastSlash(myPath);
|
|
// BEWARE! GLUT is going to put us here anyways, DO NOT TRY SOMEWHERE ELSE
|
|
// or you will waste your goddamn time like I did
|
|
// damn undocumented shit.
|
|
strcat(myPath,"/Resources");
|
|
chdir(myPath);
|
|
free(myPath);
|
|
}
|
|
#endif /* MACOSX */
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
setlocale(LC_ALL, "");
|
|
setlocale(LC_NUMERIC, "C");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
bind_textdomain_codeset(PACKAGE, "UTF-8");
|
|
textdomain(PACKAGE);
|
|
|
|
#ifdef MACOSX
|
|
#define BUNDLEONLY 1
|
|
#ifndef BUNDLEONLY
|
|
// for bundles only!
|
|
if (argc==2 && argv[1][0]=='-' && argv[1][1]=='p' && argv[1][2]=='s' && argv[1][3]=='n')
|
|
{
|
|
#endif /* !BUNDLEONLY */
|
|
argc--;
|
|
dirFixup(argv[0]);
|
|
#ifndef BUNDLEONLY
|
|
} else
|
|
/* BE REAL DAMN CAREFUL WITH THIS LINGERING IF! */
|
|
#endif /* !BUNDLEONLY */
|
|
#else /* MACOSX */
|
|
if (chdir(CONFIG_DATA_DIR) == -1)
|
|
{
|
|
cerr << "Cannot chdir to '" << CONFIG_DATA_DIR <<
|
|
"', probably due to improper installation\n";
|
|
}
|
|
#endif /* !MACOSX */
|
|
// Not ready to render yet
|
|
ready = false;
|
|
|
|
char c;
|
|
int startfile = 0;
|
|
while ((c = getopt(argc, argv, "v::f")) > -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '?':
|
|
cout << "Usage: celestia [-v] [-f <filename>]\n";
|
|
exit(1);
|
|
case 'v':
|
|
if(optarg)
|
|
SetDebugVerbosity(atoi(optarg));
|
|
else
|
|
SetDebugVerbosity(0);
|
|
break;
|
|
case 'f':
|
|
startfile = 1;
|
|
}
|
|
}
|
|
|
|
appCore = new CelestiaCore();
|
|
if (appCore == nullptr)
|
|
{
|
|
cerr << "Out of memory.\n";
|
|
return 1;
|
|
}
|
|
|
|
if (!appCore->initSimulation())
|
|
{
|
|
cerr << "Error initializing simulation.\n";
|
|
return 1;
|
|
}
|
|
|
|
glutInit(&argc, argv);
|
|
glutInitWindowSize(480, 360);
|
|
glutInitWindowPosition(0, 0);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
|
|
mainWindow = glutCreateWindow("Celestia");
|
|
|
|
Resize(480, 360);
|
|
glutReshapeFunc(Resize);
|
|
glutDisplayFunc(Display);
|
|
glutIdleFunc(Idle);
|
|
glutMouseFunc(MouseButton);
|
|
glutMotionFunc(MouseDrag);
|
|
glutKeyboardFunc(KeyPress);
|
|
glutKeyboardUpFunc(KeyUp);
|
|
glutSpecialFunc(SpecialKeyPress);
|
|
glutSpecialUpFunc(SpecialKeyUp);
|
|
|
|
#ifdef MACOSX
|
|
initMenus();
|
|
#endif
|
|
|
|
GLenum glewErr = glewInit();
|
|
if (glewErr != GLEW_OK)
|
|
{
|
|
fmt::fprintf(std::cerr,
|
|
_("Celestia was unable to initialize OpenGL extensions (error %i). Graphics quality will be reduced."),
|
|
glewErr);
|
|
}
|
|
|
|
// GL should be all set up, now initialize the renderer.
|
|
appCore->initRenderer();
|
|
|
|
appCore->getRenderer()->setSolarSystemMaxDistance(appCore->getConfig()->SolarSystemMaxDistance);
|
|
|
|
// Set the simulation starting time to the current system time
|
|
time_t curtime = time(nullptr);
|
|
appCore->start((double) curtime / 86400.0 + (double) astro::Date(1970, 1, 1));
|
|
struct tm result;
|
|
if (localtime_r(&curtime, &result))
|
|
{
|
|
appCore->setTimeZoneBias(result.tm_gmtoff);
|
|
appCore->setTimeZoneName(result.tm_zone);
|
|
}
|
|
|
|
if (startfile == 1) {
|
|
if (argv[argc - 1][0] == '-') {
|
|
cout << "Missing Filename.\n";
|
|
return 1;
|
|
}
|
|
|
|
cout << "*** Using CEL File: " << argv[argc - 1] << endl;
|
|
appCore->runScript(argv[argc - 1]);
|
|
}
|
|
|
|
ready = true;
|
|
glutMainLoop();
|
|
|
|
return 0;
|
|
}
|