556 lines
15 KiB
C++
556 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>
|
||
|
||
// celengine/glsupport.h must be included before GL/glut.h / GL/gl.h
|
||
#include <celengine/glsupport.h>
|
||
#ifndef MACOSX
|
||
#include <GL/glut.h>
|
||
#else
|
||
#include <Carbon/Carbon.h>
|
||
#include <GLUT/glut.h>
|
||
#endif
|
||
#include <fmt/printf.h>
|
||
#include <celutil/gettext.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 celestia;
|
||
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
|
||
|
||
if (!gl::init() || !gl::checkVersion(gl::GL_2_1))
|
||
{
|
||
cout << _("Celestia was unable to initialize OpenGL 2.1.\n");
|
||
return 1;
|
||
}
|
||
|
||
// 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
|
||
appCore->start();
|
||
|
||
string tzName;
|
||
int dstBias;
|
||
if (GetTZInfo(tzName, dstBias))
|
||
{
|
||
appCore->setTimeZoneName(tzName);
|
||
appCore->setTimeZoneBias(dstBias);
|
||
}
|
||
|
||
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;
|
||
}
|