1 // glutmain.cpp
2 //
3 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
4 //
5 // GLUT front-end for Celestia.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 
12 #include <iostream>
13 #include <fstream>
14 #include <algorithm>
15 #include <cstdlib>
16 #include <cctype>
17 #include <cstring>
18 #include <time.h>
19 #include <unistd.h>
20 #include <celengine/gl.h>
21 #ifndef MACOSX
22 #include <GL/glut.h>
23 #else
24 #include <Carbon/Carbon.h>
25 #include <GLUT/glut.h>
26 #endif
27 #include <celengine/celestia.h>
28 #include <celmath/vecmath.h>
29 #include <celmath/quaternion.h>
30 #include <celutil/util.h>
31 #include <celutil/debug.h>
32 #include <celmath/mathlib.h>
33 #include <celengine/astro.h>
34 #include "celestiacore.h"
35 /* what are you supposed to be?
36 #include "popt.h"
37 */
38 
39 using namespace std;
40 
41 
42 char AppName[] = "Celestia";
43 
44 static CelestiaCore* appCore = NULL;
45 
46 //static bool fullscreen = false;
47 static bool ready = false;
48 
49 
50 // Mouse motion tracking
51 static int lastX = 0;
52 static int lastY = 0;
53 static bool leftButton = false;
54 static bool middleButton = false;
55 static bool rightButton = false;
56 
57 static int mainWindow = 1;
58 
59 // Mouse wheel button assignments
60 #define MOUSE_WHEEL_UP   3
61 #define MOUSE_WHEEL_DOWN 4
62 
63 
64 
Resize(int w,int h)65 static void Resize(int w, int h)
66 {
67     appCore->resize(w, h);
68 }
69 
70 
71 /*
72  * Definition of GLUT callback functions
73  */
74 
Display(void)75 static void Display(void)
76 {
77     if (ready)
78     {
79         appCore->draw();
80         glutSwapBuffers();
81     }
82 }
83 
Idle(void)84 static void Idle(void)
85 {
86     if (glutGetWindow() != mainWindow)
87         glutSetWindow(mainWindow);
88 
89     appCore->tick();
90 
91     Display();
92 }
93 
MouseDrag(int x,int y)94 static void MouseDrag(int x, int y)
95 {
96     int buttons = 0;
97     if (leftButton)
98         buttons |= CelestiaCore::LeftButton;
99     if (rightButton)
100         buttons |= CelestiaCore::RightButton;
101     if (middleButton)
102         buttons |= CelestiaCore::MiddleButton;
103 
104     appCore->mouseMove(x - lastX, y - lastY, buttons);
105 
106     lastX = x;
107     lastY = y;
108 }
109 
MouseButton(int button,int state,int x,int y)110 static void MouseButton(int button, int state, int x, int y)
111 {
112 #ifdef MACOSX
113     if (button == GLUT_LEFT_BUTTON) {
114         UInt32 mods=GetCurrentKeyModifiers ();
115         if      (mods & optionKey)  button=GLUT_MIDDLE_BUTTON;
116         else if (mods & cmdKey)     button=GLUT_RIGHT_BUTTON;
117     }
118 #endif /* MACOSX */
119     // On Linux, mouse wheel up and down are usually translated into
120     // mouse button 4 and 5 down events.
121     if (button == MOUSE_WHEEL_UP)
122     {
123         appCore->mouseWheel(-1.0f, 0);
124     }
125     else if (button == MOUSE_WHEEL_DOWN)
126     {
127         appCore->mouseWheel(1.0f, 0);
128     }
129     else if (button == GLUT_LEFT_BUTTON)
130     {
131         leftButton = (state == GLUT_DOWN);
132         if (state == GLUT_DOWN)
133             appCore->mouseButtonDown(x, y, CelestiaCore::LeftButton);
134         else
135             appCore->mouseButtonUp(x, y, CelestiaCore::LeftButton);
136     }
137     else if (button == GLUT_RIGHT_BUTTON)
138     {
139         rightButton = (state == GLUT_DOWN);
140         if (state == GLUT_DOWN)
141             appCore->mouseButtonDown(x, y, CelestiaCore::RightButton);
142         else
143             appCore->mouseButtonUp(x, y, CelestiaCore::RightButton);
144     }
145     else if (button == GLUT_MIDDLE_BUTTON)
146     {
147         middleButton = (state == GLUT_DOWN);
148         if (state == GLUT_DOWN)
149             appCore->mouseButtonDown(x, y, CelestiaCore::MiddleButton);
150         else
151             appCore->mouseButtonUp(x, y, CelestiaCore::MiddleButton);
152     }
153 
154     lastX = x;
155     lastY = y;
156 }
157 
158 
KeyPress(unsigned char c,int x,int y)159 static void KeyPress(unsigned char c, int x, int y)
160 {
161     // Ctrl-Q exits
162     if (c == '\021')
163         exit(0);
164 
165     appCore->charEntered((char) c);
166     //appCore->keyDown((int) c);
167 }
168 
169 
KeyUp(unsigned char c,int x,int y)170 static void KeyUp(unsigned char c, int x, int y)
171 {
172     appCore->keyUp((int) c);
173 }
174 
175 
HandleSpecialKey(int key,bool down)176 static void HandleSpecialKey(int key, bool down)
177 {
178     int k = -1;
179     switch (key)
180     {
181     case GLUT_KEY_UP:
182         k = CelestiaCore::Key_Up;
183         break;
184     case GLUT_KEY_DOWN:
185         k = CelestiaCore::Key_Down;
186         break;
187     case GLUT_KEY_LEFT:
188         k = CelestiaCore::Key_Left;
189         break;
190     case GLUT_KEY_RIGHT:
191         k = CelestiaCore::Key_Right;
192         break;
193     case GLUT_KEY_HOME:
194         k = CelestiaCore::Key_Home;
195         break;
196     case GLUT_KEY_END:
197         k = CelestiaCore::Key_End;
198         break;
199     case GLUT_KEY_F1:
200         k = CelestiaCore::Key_F1;
201         break;
202     case GLUT_KEY_F2:
203         k = CelestiaCore::Key_F2;
204         break;
205     case GLUT_KEY_F3:
206         k = CelestiaCore::Key_F3;
207         break;
208     case GLUT_KEY_F4:
209         k = CelestiaCore::Key_F4;
210         break;
211     case GLUT_KEY_F5:
212         k = CelestiaCore::Key_F5;
213         break;
214     case GLUT_KEY_F6:
215         k = CelestiaCore::Key_F6;
216         break;
217     case GLUT_KEY_F7:
218         k = CelestiaCore::Key_F7;
219         break;
220     case GLUT_KEY_F11:
221         k = CelestiaCore::Key_F11;
222         break;
223     case GLUT_KEY_F12:
224         k = CelestiaCore::Key_F12;
225         break;
226     }
227     /* Glut doesn't seem to handle Keypad Keys, so we can't pass them on.
228        They will be passed as the appropriate Special keys instead */
229 
230     if (k >= 0)
231     {
232         if (down)
233             appCore->keyDown(k);
234         else
235             appCore->keyUp(k);
236     }
237 }
238 
239 
SpecialKeyPress(int key,int x,int y)240 static void SpecialKeyPress(int key, int x, int y)
241 {
242     HandleSpecialKey(key, true);
243 }
244 
245 
SpecialKeyUp(int key,int x,int y)246 static void SpecialKeyUp(int key, int x, int y)
247 {
248     HandleSpecialKey(key, false);
249 }
250 
251 #ifdef MACOSX
252 static void menuCallback (int which);
initMenus(void)253 static void initMenus(void) {
254     int gMain,gNavigation,gTime,gLabels,gRendering;
255     int gViews,gSpaceflight,gNumber,gJoystick,gMouse;
256     gMain=gNavigation=gTime=gLabels=gRendering=0;
257     gViews=gSpaceflight=gNumber=gJoystick=gMouse=0;
258     gNavigation=glutCreateMenu(menuCallback);
259     glutAddMenuEntry("Center                      C",101);
260     glutAddMenuEntry("Go closer                   G",102);
261     glutAddMenuEntry("Follow                      F",103);
262     glutAddMenuEntry("Orbit                       Y",104);
263     glutAddMenuEntry("Track                       T",105);
264     glutAddMenuEntry("Move closer              HOME",106);
265     glutAddMenuEntry("Move farther              END",107);
266     glutAddMenuEntry("Cancel motion             ESC",108);
267     glutAddMenuEntry("*Roll Camera            <- ->",000);
268     glutAddMenuEntry("*Camera Pitch         UP DOWN",000);
269     gTime=glutCreateMenu(menuCallback);
270     glutAddMenuEntry("10x faster                  L",201);
271     glutAddMenuEntry("10x slower                  K",202);
272     glutAddMenuEntry("Reverse time                J",203);
273     gLabels=glutCreateMenu(menuCallback);
274     glutAddMenuEntry("Toggle planet/moon          N",301);
275     glutAddMenuEntry("Toggle star                 B",302);
276     glutAddMenuEntry("Toggle constellation        =",303);
277     glutAddMenuEntry("Toggle info text            V",304);
278     gRendering=glutCreateMenu(menuCallback);
279     glutAddMenuEntry("Wireframe                   W",401);
280     glutAddMenuEntry("Per-pixel lighting     CTRL+P",402);
281     glutAddMenuEntry("Vertex programs        CTRL+V",403);
282     glutAddMenuEntry("Show FPS                    `",404);
283     glutAddMenuEntry("*Limiting magnitude       ] [",000);
284     glutAddMenuEntry("*Ambient illumination     } {",000);
285     glutAddMenuEntry("*Narrow/Widen FOV         , .",000);
286     gViews=glutCreateMenu(menuCallback);
287     glutAddMenuEntry("Galaxies                    U",501);
288     glutAddMenuEntry("Planet orbits               O",502);
289     glutAddMenuEntry("Constellations              /",503);
290     glutAddMenuEntry("Atmospheres            CTRL+A",504);
291     glutAddMenuEntry("Cloud textures              I",505);
292     glutAddMenuEntry("Night side planet maps CTRL+L",506);
293     glutAddMenuEntry("Equatorial coordinates      ;",507);
294     gSpaceflight=glutCreateMenu(menuCallback);
295     glutAddMenuEntry("Stop                       F1",601);
296     glutAddMenuEntry("Set velocity to 1 km/s     F2",602);
297     glutAddMenuEntry("Set velocity to 1,000 km/s F3",603);
298     glutAddMenuEntry("Set velocity to lightspeed F4",604);
299     glutAddMenuEntry("Set velocity to 10^6 km/s  F5",605);
300     glutAddMenuEntry("Set velocity to 1 AU/s     F6",606);
301     glutAddMenuEntry("Set velocity to 1 ly/s     F7",607);
302     glutAddMenuEntry("Increase velocity (exp)     A",608);
303     glutAddMenuEntry("Decrease velocity (exp)     Z",609);
304     glutAddMenuEntry("Reverse direction           Q",610);
305     glutAddMenuEntry("Movement to screen origin   X",611);
306     gNumber=glutCreateMenu(menuCallback);
307     glutAddMenuEntry("Stop rotation               5",701);
308     glutAddMenuEntry("*Yaw left/right           4 6",702);
309     glutAddMenuEntry("*Pitch up/down            2 8",703);
310     glutAddMenuEntry("*Roll left/right          7 9",704);
311     gJoystick=glutCreateMenu(menuCallback);
312     glutAddMenuEntry("Enable joystick            F8",801);
313     glutAddMenuEntry("*Yaw                   X axis",000);
314     glutAddMenuEntry("*Pitch                 Y axis",000);
315     glutAddMenuEntry("*Roll             L,R trigger",000);
316     glutAddMenuEntry("*Speed             Button 1,2",000);
317     gMain=glutCreateMenu(menuCallback);
318     glutAddMenuEntry("Select the sun (Home)    H",001);
319 /*    glutAddMenuEntry("Select by name       ENTER",002); */
320     glutAddMenuEntry("Run demo                         D",003);
321 /*
322     gMouse=glutCreateMenu(menuCallback);
323     glutAddSubMenu("*Orient camera    CLICK+DRAG",000);
324     glutAddSubMenu("*Orbit object    RCLICK+DRAG",000);
325 */
326     glutAddSubMenu("Selected Object", gNavigation);
327     glutAddSubMenu("Time",gTime);
328     glutAddSubMenu("Labels",gLabels);
329     glutAddSubMenu("Rendering",gRendering);
330     glutAddSubMenu("Views",gViews);
331     glutAddSubMenu("Spaceflight",gSpaceflight);
332     glutAddSubMenu("Number Pad",gNumber);
333     glutAddSubMenu("Joystick",gJoystick);
334 /*
335     glutAddSubMenu("Mouse",gMouse);
336 */
337     glutAttachMenu(GLUT_RIGHT_BUTTON);
338 }
339 
340 #define CTRLKEY(KEY) (KEY-'a'+(char)1)
341 #define caseKey(CASE,WHICH) \
342     case (CASE): \
343         appCore->charEntered((char)(WHICH)); \
344         appCore->keyDown((int)(WHICH)); \
345         appCore->keyUp((int)(WHICH)); \
346         break
347 #define caseKeySpecial(CASE,WHICH) \
348     case (CASE): \
349         appCore->keyDown(CelestiaCore::Key_##WHICH); \
350         appCore->keyUp(CelestiaCore::Key_##WHICH); \
351         break
352 
menuCallback(int which)353 static void menuCallback (int which) {
354     switch(which) {
355         // main menu
356         caseKey(1,'h');
357         caseKey(2,0x13);
358         caseKey(3,'d');
359         // navigation
360         caseKey(101,'c');
361         caseKey(102,'g');
362         caseKey(103,'f');
363         caseKey(104,'y');
364         caseKey(105,'t');
365         caseKeySpecial(106,Home);
366         caseKeySpecial(107,End);
367         caseKey(108,0x27);
368         // time
369         caseKey(201,'l');
370         caseKey(202,'k');
371         caseKey(203,'j');
372         // labels
373         caseKey(301,'n');
374         caseKey(302,'b');
375         caseKey(303,'=');
376         caseKey(304,'v');
377         // rendering
378         caseKey(401,'w');
379         caseKey(402,CTRLKEY('p'));
380         caseKey(403,CTRLKEY('v'));
381         caseKey(404,'`');
382         // views
383         caseKey(501,'u');
384         caseKey(502,'o');
385         caseKey(503,'/');
386         caseKey(504,CTRLKEY('a'));
387         caseKey(505,'i');
388         caseKey(506,CTRLKEY('l'));
389         caseKey(507,';');
390         // spaceflight
391         caseKeySpecial(601,F1);
392         caseKeySpecial(602,F2);
393         caseKeySpecial(603,F3);
394         caseKeySpecial(604,F4);
395         caseKeySpecial(605,F5);
396         caseKeySpecial(606,F6);
397         caseKeySpecial(607,F7);
398         caseKey(608,'a');
399         caseKey(609,'z');
400         caseKey(610,'q');
401         caseKey(611,'x');
402         // number pad
403         caseKey(701,5);
404         // joystick
405         caseKeySpecial(801,F8);
406     }
407 }
408 
409 
410 #endif
411 
412 #ifdef MACOSX
killLastSlash(char * buf)413 static void killLastSlash(char *buf) {
414   int i=strlen(buf);
415   while (--i && buf[i]!='/') {}
416   if (buf[i]=='/') buf[i]=0;
417 }
dirFixup(char * argv0)418 static void dirFixup(char *argv0) {
419     char *myPath;
420     assert(myPath=(char *)malloc(strlen(argv0)+128));
421     strcpy(myPath,argv0);
422     killLastSlash(myPath);
423     killLastSlash(myPath);
424     // BEWARE!  GLUT is going to put us here anyways, DO NOT TRY SOMEWHERE ELSE
425     // or you will waste your goddamn time like I did
426     // damn undocumented shit.
427     strcat(myPath,"/Resources");
428     chdir(myPath);
429     free(myPath);
430 }
431 #endif /* MACOSX */
432 
main(int argc,char * argv[])433 int main(int argc, char* argv[])
434 {
435 	setlocale(LC_ALL, "");
436 	setlocale(LC_NUMERIC, "C");
437 	bindtextdomain(PACKAGE, LOCALEDIR);
438 	bind_textdomain_codeset(PACKAGE, "UTF-8");
439 	textdomain(PACKAGE);
440 
441    #ifdef MACOSX
442    #define BUNDLEONLY 1
443    #ifndef BUNDLEONLY
444    // for bundles only!
445    if (argc==2 && argv[1][0]=='-' && argv[1][1]=='p' && argv[1][2]=='s' && argv[1][3]=='n')
446    {
447    #endif /* !BUNDLEONLY */
448         argc--;
449         dirFixup(argv[0]);
450     #ifndef BUNDLEONLY
451     } else
452     /* BE REAL DAMN CAREFUL WITH THIS LINGERING IF! */
453     #endif /* !BUNDLEONLY */
454     #else /* MACOSX */
455     if (chdir(CONFIG_DATA_DIR) == -1)
456     {
457         cerr << "Cannot chdir to '" << CONFIG_DATA_DIR <<
458             "', probably due to improper installation\n";
459     }
460     #endif /* !MACOSX */
461     // Not ready to render yet
462     ready = false;
463 
464     char c;
465 	int startfile = 0;
466     while ((c = getopt(argc, argv, "v::f")) > -1)
467     {
468         if (c == '?')
469         {
470             cout << "Usage: celestia [-v] [-f <filename>]\n";
471             exit(1);
472         }
473         else if (c == 'v')
474         {
475             if(optarg)
476                 SetDebugVerbosity(atoi(optarg));
477             else
478                 SetDebugVerbosity(0);
479         }
480 		else if (c == 'f') {
481 			startfile = 1;
482 		}
483     }
484 
485     appCore = new CelestiaCore();
486     if (appCore == NULL)
487     {
488         cerr << "Out of memory.\n";
489         return 1;
490     }
491 
492     if (!appCore->initSimulation())
493     {
494         return 1;
495     }
496 
497     glutInit(&argc, argv);
498     glutInitWindowSize(480, 360);
499     glutInitWindowPosition(0, 0);
500     glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
501     mainWindow = glutCreateWindow("Celestia");
502 
503     Resize(480, 360);
504     glutReshapeFunc(Resize);
505     glutDisplayFunc(Display);
506     glutIdleFunc(Idle);
507     glutMouseFunc(MouseButton);
508     glutMotionFunc(MouseDrag);
509     glutKeyboardFunc(KeyPress);
510     glutKeyboardUpFunc(KeyUp);
511     glutSpecialFunc(SpecialKeyPress);
512     glutSpecialUpFunc(SpecialKeyUp);
513 
514     #ifdef MACOSX
515     initMenus();
516     #endif
517 
518     // GL should be all set up, now initialize the renderer.
519     appCore->initRenderer();
520 
521     // Set the simulation starting time to the current system time
522     time_t curtime=time(NULL);
523     appCore->start((double) curtime / 86400.0 + (double) astro::Date(1970, 1, 1));
524     #if defined(MACOSX) || defined(__FreeBSD__)
525     /* localtime in Darwin is reentrant only
526        equiv to Linux localtime_r()
527        should probably port !MACOSX code to use this too, available since
528        libc 5.2.5 according to manpage */
529     struct tm *temptime=localtime(&curtime);
530     appCore->setTimeZoneBias(temptime->tm_gmtoff);
531     appCore->setTimeZoneName(temptime->tm_zone);
532     #else
533     localtime(&curtime); // Only doing this to set timezone as a side effect
534     appCore->setTimeZoneBias(-timezone);
535     appCore->setTimeZoneName(tzname[daylight?0:1]);
536     #endif
537 
538 	if (startfile == 1) {
539 		if (argv[argc - 1][0] == '-') {
540 			cout << "Missing Filename.\n";
541 			return 1;
542 		}
543 
544 		cout << "*** Using CEL File: " << argv[argc - 1] << endl;
545 		appCore->runScript(argv[argc - 1]);
546 	}
547 
548     ready = true;
549     glutMainLoop();
550 
551     return 0;
552 }
553 
554 
555