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