CelestiaContent/src/celestia/glut/glutmain.cpp

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;
}