1 
2 // app.cpp [pengine]
3 
4 // Copyright 2004-2006 Jasmine Langridge, jas@jareiko.net
5 // License: GPL version 2 (see included gpl.txt)
6 
7 
8 #include "pengine.h"
9 #include "physfs_utils.h"
10 
11 #ifdef WIN32
12 #define WIN32_LEAN_AND_MEAN
13 #include <windows.h>
14 #endif
15 
16 #if 0
17 #ifndef DATADIR
18 //#error DATADIR not defined! Use ./configure --datadir=...
19 //#define DATADIR "../data"
20 #endif
21 #endif
22 
23 #if 0
24 #ifndef LOCALSTATEDIR
25 #warning LOCALSTATEDIR not defined! Will attempt to determine at run time.
26 #warning Use ./configure --localstatedir=...
27 #endif
28 #endif
29 
30 
31 /*
32 FIXME: not bothering to close joysticks because I
33 suspect SDL does it for you on quit. Am I right?
34 */
35 
36 
37 int PUtil::deblev = DEBUGLEVEL_ENDUSER;
38 
39 
40 
setScreenModeAutoWindow()41 void PApp::setScreenModeAutoWindow()
42 {
43 #ifdef WIN32
44   cy = GetSystemMetrics(SM_CYSCREEN) * 6 / 7;
45   cx = cy * 4 / 3;
46 #else
47   //#error COMPLETE FOR PLATFORM
48   cy = 1024 * 6 / 7;
49   cx = cy * 4 / 3;
50 #endif
51 
52   fullscr = false;
53 }
54 
setScreenModeFastFullScreen()55 void PApp::setScreenModeFastFullScreen()
56 {
57 #ifdef WIN32
58   cx = GetSystemMetrics(SM_CXSCREEN);
59   cy = GetSystemMetrics(SM_CYSCREEN);
60 #else
61   //#error COMPLETE FOR PLATFORM
62   cx = 1280;
63   cy = 1024;
64 #endif
65 
66   fullscr = false;
67 
68   noframe = true;
69 }
70 
71 /* This routine performs the perspective projection for one eye's subfield.
72    The projection is in the direction of the negative z axis.
73 
74    xmin, ymax, ymin, ymax = the coordinate range, in the plane of zero
75    parallax setting, that will be displayed on the screen. The ratio between
76    (xmax-xmin) and (ymax-ymin) should equal the aspect ration of the display.
77 
78    znear, zfar = the z-coordinate values of the clipping planes.
79 
80    zzps = the z-coordinate of the plane of zero parallax setting.
81 
82    dist = the distance from the center of projection to the plane of zero
83    parallax.
84 
85    eye = half the eye separation; positive for the right eye subfield,
86    negative for the left eye subfield.
87 */
stereoGLProject(float xmin,float xmax,float ymin,float ymax,float znear,float zfar,float zzps,float dist,float eye)88 void PApp::stereoGLProject(float xmin, float xmax, float ymin, float ymax, float znear, float zfar, float zzps, float dist, float eye)
89 {
90   float xmid, ymid, clip_near, clip_far, top, bottom, left, right, dx, dy, n_over_d;
91 
92   dx = xmax - xmin;
93   dy = ymax - ymin;
94 
95   xmid = (xmax + xmin) / 2.0;
96   ymid = (ymax + ymin) / 2.0;
97 
98   clip_near = dist + zzps - znear;
99   clip_far = dist + zzps - zfar;
100 
101   n_over_d = clip_near / dist;
102 
103   top = n_over_d * dy / 2.0;
104   bottom = -top;
105   right = n_over_d * (dx / 2.0 - eye);
106   left = n_over_d * (-dx / 2.0 - eye);
107 
108   glMatrixMode(GL_PROJECTION);
109   glLoadIdentity();
110   glFrustum(left, right, bottom, top, clip_near, clip_far);
111   glTranslatef(-xmid - eye, -ymid, -zzps - dist);
112 }
113 
114 // I'm afraid I didn't understand how stereoGLProject worked, so I rewrote it
115 
stereoFrustum(float xmin,float xmax,float ymin,float ymax,float znear,float zfar,float zzps,float eye)116 void PApp::stereoFrustum(float xmin, float xmax, float ymin, float ymax, float znear, float zfar, float zzps, float eye)
117 {
118   // xmove = eye * (zzps - znear) / zzps - eye, simplifies to
119 
120   float xmove = -eye * znear / zzps;
121 
122   glFrustum(xmin + xmove, xmax + xmove, ymin, ymax, znear, zfar);
123 }
124 
run(int argc,char * argv[])125 int PApp::run(int argc, char *argv[])
126 {
127   PUtil::outLog() << apptitle << " init" << std::endl;
128 
129   PUtil::outLog() << "Build: " << PACKAGE_VERSION << " on " << __DATE__ << " at " << __TIME__ << std::endl;
130 
131   if (exit_requested) {
132     PUtil::outLog() << "Exit requested" << std::endl;
133     return 0;
134   }
135 
136   PUtil::outLog() << "Initialising PhysFS" << std::endl;
137 
138   if (PHYSFS_init((argc >= 1) ? argv[0] : nullptr) == 0) {
139     PUtil::outLog() << "PhysFS failed to initialise" << std::endl;
140     PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
141     return 1;
142   }
143 
144   PHYSFS_permitSymbolicLinks(1);
145 
146   {
147     std::string lsdbuff;
148 
149     lsdbuff = physfs_getDir();
150 
151     PUtil::outLog() << "Setting writable user directory to \"" << lsdbuff << "\"" << std::endl;
152 
153     if (PHYSFS_setWriteDir(lsdbuff.c_str()) == 0) {
154       PUtil::outLog() << "Failed to set PhysFS writable directory to \"" << lsdbuff << "\"" << std::endl
155           << "PhysFS: " << physfs_getErrorString() << std::endl;
156     }
157 
158     if (PHYSFS_mkdir("/players") == 0)
159     {
160         PUtil::outLog() << "Failed to create directory \"/players\"" << std::endl
161             << "PhysFS: " << physfs_getErrorString() << std::endl;
162     }
163 
164     std::string basedir = PHYSFS_getBaseDir();
165     PUtil::outLog() << "Application base directory \"" << basedir << '\"' << std::endl;
166     if (PHYSFS_mount(basedir.c_str(), NULL, 1) == 0) {
167       PUtil::outLog() << "Failed to add PhysFS search directory \"" << basedir << "\"" << std::endl
168           << "PhysFS: " << physfs_getErrorString() << std::endl;
169     }
170 
171     if (PHYSFS_mount(lsdbuff.c_str(), NULL, 1) == 0) {
172       PUtil::outLog() << "Failed to add PhysFS search directory \"" << lsdbuff << "\"" << std::endl
173           << "PhysFS: " << physfs_getErrorString() << std::endl;
174     }
175 
176     // we run MainApp::config() here in order to add data dirs to PhysFS search path
177     try
178     {
179         config();
180     }
181     catch (PException &e)
182     {
183         PUtil::outLog() << "Config failed: " << e.what() << std::endl;
184 
185         if (PHYSFS_deinit() == 0)
186         {
187             PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
188         }
189 
190         return 1;
191     }
192 
193     // Find any .zip files and add them to search path
194     std::list<std::string> zipfiles = PUtil::findFiles("", ".zip");
195 
196     for (std::list<std::string>::iterator i = zipfiles.begin();
197       i != zipfiles.end(); ++i) {
198 
199       const char *realpath = PHYSFS_getRealDir(i->c_str());
200 
201       if (realpath) {
202         std::string fullpath = (std::string)realpath + *i;
203 
204         if (PHYSFS_mount(fullpath.c_str(), NULL, 1) == 0) {
205           PUtil::outLog() << "Failed to add archive \"" << fullpath << "\"" << std::endl
206               << "PhysFS: " << physfs_getErrorString() << std::endl;
207         }
208       } else {
209         PUtil::outLog() << "Failed to find path of archive \"" << *i << "\"" << std::endl
210             << "PhysFS: " << physfs_getErrorString() << std::endl;
211       }
212     }
213   }
214 
215   srand(SDL_GetTicks());
216 
217   PUtil::outLog() << "Create window and set video mode" << std::endl;
218 #if 0
219     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
220     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
221     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
222     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
223 #endif
224   SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
225 
226   if (reqRGB) {
227     SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
228     SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
229     SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
230   }
231 
232   if (reqAlpha) {
233     SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
234   }
235 
236   if (reqDepth) {
237     SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
238   }
239 
240   if (reqStencil)
241     SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 );
242 
243   if (stereo == StereoQuadBuffer) {
244     SDL_GL_SetAttribute( SDL_GL_STEREO, 1 );
245   }
246 
247   if (cx <= 0 || cy <= 0) setScreenModeAutoWindow();
248 
249     if (!autoVideo)
250     {
251         screen = SDL_CreateWindow(apptitle.c_str(),
252             SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
253             cx, cy,
254             SDL_WINDOW_OPENGL |
255             (fullscr ? SDL_WINDOW_FULLSCREEN : 0)
256             /* | (noframe ? SDL_NOFRAME : 0) */ ); // broken by SDL2, no more `SDL_NOFRAME`
257     }
258     else // automatic video mode
259     {
260         //
261         // NOTE:
262         //  The `SDL_GetDesktopMode()` function is used instead of the automatic
263         //  flag `SDL_WINDOW_FULLSCREEN_DESKTOP` because the latter fails to work
264         //  reliably on Linux in certain versions of SDL2.
265         //
266 
267 #define SDL_WINDOW_FULLSCREEN_DESKTOP_IS_STILL_BROKEN
268 
269 #ifdef SDL_WINDOW_FULLSCREEN_DESKTOP_IS_STILL_BROKEN
270         SDL_DisplayMode dm;
271 
272         if (SDL_GetDesktopDisplayMode(0, &dm) == 0)
273         {
274             cx = dm.w;
275             cy = dm.h;
276             PUtil::outLog() << "Desktop video mode resolution: " << cx << 'x' << cy << std::endl;
277         }
278         else
279         {
280             PUtil::outLog() << "SDL error, SDL_GetDesktopDisplayMode(): " << SDL_GetError() << std::endl;
281             cx = 800;
282             cy = 600;
283         }
284 
285         screen = SDL_CreateWindow(apptitle.c_str(),
286             SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
287             cx, cy,
288             SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
289 #else
290         //
291         // NOTE:
292         //  This would be the "proper" way of doing things, but at the time of this
293         //  writing it only works reliably on Windows 7 using SDL2 version 2.0.5.
294         //
295         screen = SDL_CreateWindow(apptitle.c_str(),
296             SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
297             0, 0,
298             SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP);
299 #endif
300     }
301 
302   if (!screen) {
303     PUtil::outLog() << "Failed to create window or set video mode" << std::endl;
304     PUtil::outLog() << "SDL error: " << SDL_GetError() << std::endl;
305     PUtil::outLog() << "Try changing your video settings in trigger-rally.config" << std::endl;
306     SDL_Quit();
307     if (PHYSFS_deinit() == 0) {
308       PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
309     }
310     return 1;
311   }
312 
313   context = SDL_GL_CreateContext(screen);
314 
315     if (context == NULL)
316     {
317         PUtil::outLog() << "Failed to create OpenGL context for game window\n";
318         PUtil::outLog() << "SDL error: " << SDL_GetError() << std::endl;
319         SDL_DestroyWindow(screen);
320         SDL_Quit();
321 
322         if (PHYSFS_deinit() == 0)
323 		{
324             PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
325 		}
326         return 1;
327     }
328 
329   sdl_mousemap = 0;
330 
331   sdl_joy.resize(SDL_NumJoysticks());
332 
333   PUtil::outLog() << "Found " << sdl_joy.size() << " joystick" <<
334     (sdl_joy.size() == 1 ? "" : "s") << std::endl;
335 
336   for (unsigned int i=0; i<sdl_joy.size(); i++) {
337     PUtil::outLog() << "Joystick " << (i+1) << ": ";
338     sdl_joy[i].sdl_joystick = SDL_JoystickOpen(i);
339     if (sdl_joy[i].sdl_joystick == nullptr) {
340       PUtil::outLog() << "failed to open joystick" << std::endl;
341       SDL_GL_DeleteContext(context);
342       SDL_DestroyWindow(screen);
343       SDL_Quit();
344       if (PHYSFS_deinit() == 0) {
345         PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
346       }
347       return 1;
348     }
349     sdl_joy[i].name = SDL_JoystickName(sdl_joy[i].sdl_joystick);
350     sdl_joy[i].axis.resize(SDL_JoystickNumAxes(sdl_joy[i].sdl_joystick));
351     for (unsigned int j=0; j<sdl_joy[i].axis.size(); j++)
352       sdl_joy[i].axis[j] = ((float)SDL_JoystickGetAxis(sdl_joy[i].sdl_joystick, j) + 0.5f) / 32767.5f;
353     sdl_joy[i].button.resize(SDL_JoystickNumButtons(sdl_joy[i].sdl_joystick));
354     for (unsigned int j=0; j<sdl_joy[i].button.size(); j++)
355       sdl_joy[i].button[j] = (SDL_JoystickGetButton(sdl_joy[i].sdl_joystick, j) != 0);
356     sdl_joy[i].hat.resize(SDL_JoystickNumHats(sdl_joy[i].sdl_joystick));
357     for (unsigned int j=0; j<sdl_joy[i].hat.size(); j++) {
358       Uint8 state = SDL_JoystickGetHat(sdl_joy[i].sdl_joystick, j);
359       sdl_joy[i].hat[j] = vec2i::zero();
360       if (state & SDL_HAT_RIGHT) sdl_joy[i].hat[j].x = 1;
361       else if (state & SDL_HAT_LEFT) sdl_joy[i].hat[j].x = -1;
362       if (state & SDL_HAT_UP) sdl_joy[i].hat[j].y = 1;
363       else if (state & SDL_HAT_DOWN) sdl_joy[i].hat[j].y = -1;
364     }
365 
366     PUtil::outLog() << sdl_joy[i].name << ", " <<
367       sdl_joy[i].axis.size() << " axis, " <<
368       sdl_joy[i].button.size() << " button, " <<
369       sdl_joy[i].hat.size() << " hat" << std::endl;
370   }
371 
372   SDL_JoystickEventState(SDL_ENABLE);
373 
374   int err = glewInit();
375 
376   if (err != GLEW_OK) {
377     PUtil::outLog() << "GLEW failed to initialise: " << glewGetErrorString(err) << std::endl;
378     SDL_GL_DeleteContext(context);
379     SDL_DestroyWindow(screen);
380     SDL_Quit();
381     if (PHYSFS_deinit() == 0) {
382       PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
383     }
384     return 1;
385   }
386 
387   PUtil::outLog() << "GLEW initialized" << std::endl;
388 
389   PUtil::outLog() << "Graphics: " <<
390     glGetString(GL_VENDOR) << " " << glGetString(GL_RENDERER) << std::endl;
391 
392   PUtil::outLog() << "Using OpenGL " << glGetString(GL_VERSION) << std::endl;
393 
394   switch (stereo) {
395   default: break;
396   case StereoQuadBuffer:
397     PUtil::outLog() << "Using hardware quad buffer stereo, separation ="
398       << stereoEyeTranslation * 2.0f << std::endl;
399     break;
400   case StereoRedBlue:
401     PUtil::outLog() << "Using red-blue anaglyph stereo, separation = "
402       << stereoEyeTranslation * 2.0f << std::endl;
403     break;
404   case StereoRedGreen:
405     PUtil::outLog() << "Using red-green anaglyph stereo, separation = "
406       << stereoEyeTranslation * 2.0f << std::endl;
407     break;
408   case StereoRedCyan:
409     PUtil::outLog() << "Using red-cyan anaglyph stereo, separation = "
410       << stereoEyeTranslation * 2.0f << std::endl;
411     break;
412   case StereoYellowBlue:
413     PUtil::outLog() << "Using yellow-blue anaglyph stereo, separation = "
414       << stereoEyeTranslation * 2.0f << std::endl;
415     break;
416   }
417 
418   std::list<PSubsystem *> sslist;
419 
420   try
421   {
422     sslist.push_back(ssrdr = new PSSRender(*this));
423     sslist.push_back(sstex = new PSSTexture(*this));
424     sslist.push_back(ssfx = new PSSEffect(*this));
425     sslist.push_back(ssmod = new PSSModel(*this));
426     sslist.push_back(ssaud = new PSSAudio(*this));
427   }
428   catch (PException &e)
429   {
430     PUtil::outLog () << "Subsystem failed to init: " << e.what() << std::endl;
431 
432     while (!sslist.empty())
433     {
434       delete sslist.back();
435       sslist.pop_back();
436     }
437 
438     SDL_GL_DeleteContext(context);
439     SDL_DestroyWindow(screen);
440     SDL_Quit();
441 
442     if (PHYSFS_deinit() == 0)
443     {
444       PUtil::outLog () << "PhysFS: " << physfs_getErrorString() << std::endl;
445     }
446 
447     return 1;
448   }
449 
450   PUtil::outLog() << "Performing app load" << std::endl;
451 
452   try
453   {
454     load();
455   }
456   catch (PException &e)
457   {
458     PUtil::outLog() << "App load failed: " << e.what () << std::endl;
459 
460     while (!sslist.empty()) {
461       delete sslist.back();
462       sslist.pop_back();
463     }
464 
465     SDL_GL_DeleteContext(context);
466     SDL_DestroyWindow(screen);
467     SDL_Quit();
468 
469     if (PHYSFS_deinit() == 0) {
470       PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
471     }
472 
473     return 1;
474   }
475 
476   //SDL_ShowCursor(SDL_DISABLE);
477   //SDL_WM_GrabInput(SDL_GRAB_ON);
478   //SDL_WM_GrabInput(SDL_GRAB_OFF);
479   //SDL_SetWindowGrab(screen, SDL_TRUE);
480   //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
481   //SDL_EnableUNICODE(1);
482 
483   sdl_keymap = SDL_GetKeyboardState(&sdl_numkeys);
484 
485   resize();
486 
487   PUtil::outLog() << "Initialisation complete, entering main loop" << std::endl;
488 
489   srand(rand() + SDL_GetTicks());
490 
491   bool active = true, repaint = true;
492   uint32 curtime = SDL_GetTicks() - 1;
493 
494   while (1) {
495     SDL_Event event;
496 
497     while ( SDL_PollEvent(&event) ) {
498       switch(event.type) {
499 
500       // Using ACTIVEEVENT only seems to cause trouble.
501 
502       /*
503       case SDL_ACTIVEEVENT:
504         active = event.active.gain;
505         if (active) {
506           SDL_ShowCursor(SDL_DISABLE);
507           //SDL_WM_GrabInput(SDL_GRAB_ON);
508           PUtil::outLog() << "Window made active" << std::endl;
509         } else {
510           SDL_ShowCursor(SDL_ENABLE);
511           //SDL_WM_GrabInput(SDL_GRAB_OFF);
512           PUtil::outLog() << "Window made inactive" << std::endl;
513         }
514         break;
515       */
516 
517       // unavailable (and unneeded?) in SDL2
518 #if 0
519       case SDL_VIDEOEXPOSE:
520         repaint = true;
521         break;
522 #endif
523 
524       case SDL_KEYDOWN:
525       case SDL_KEYUP:
526         keyEvent(event.key);
527         break;
528 
529       case SDL_MOUSEMOTION:
530         if (grabinput)
531           mouseMoveEvent(event.motion.xrel, -event.motion.yrel);
532         else
533           cursorMoveEvent(event.motion.x, event.motion.y);
534         break;
535 
536       case SDL_MOUSEBUTTONDOWN:
537       case SDL_MOUSEBUTTONUP:
538         mouseButtonEvent(event.button);
539         break;
540 
541       case SDL_JOYAXISMOTION:
542         sdl_joy[event.jaxis.which].axis[event.jaxis.axis] =
543           ((float)event.jaxis.value + 0.5f) / 32767.5f;
544         break;
545 
546       case SDL_JOYBUTTONDOWN:
547         sdl_joy[event.jbutton.which].button[event.jbutton.button] = true;
548         joyButtonEvent(event.jbutton.which,event.jbutton.button,true);
549         break;
550 
551       case SDL_JOYBUTTONUP:
552         sdl_joy[event.jbutton.which].button[event.jbutton.button] = false;
553         joyButtonEvent(event.jbutton.which,event.jbutton.button,false);
554         break;
555 
556       case SDL_JOYHATMOTION:
557         sdl_joy[event.jhat.which].hat[event.jhat.hat] = vec2i::zero();
558         if (event.jhat.value & SDL_HAT_RIGHT) sdl_joy[event.jhat.which].hat[event.jhat.hat].x = 1;
559         else if (event.jhat.value & SDL_HAT_LEFT) sdl_joy[event.jhat.which].hat[event.jhat.hat].x = -1;
560         if (event.jhat.value & SDL_HAT_UP) sdl_joy[event.jhat.which].hat[event.jhat.hat].y = 1;
561         else if (event.jhat.value & SDL_HAT_DOWN) sdl_joy[event.jhat.which].hat[event.jhat.hat].y = -1;
562         break;
563 
564       case SDL_QUIT:
565         requestExit();
566         break;
567       }
568       if (exit_requested) break;
569     }
570 
571     if (exit_requested) {
572       break;
573     }
574 
575     sdl_mousemap = SDL_GetMouseState(nullptr, nullptr);
576 
577 #define TIMESCALE 1.0
578 
579     uint32 nowtime = SDL_GetTicks();
580 
581     if (1) {//if (active) {
582       uint32 timepassed = nowtime - curtime;
583       if (timepassed > 100) timepassed = 100;
584       if (timepassed > 0) {
585         float delta = (float)timepassed * 0.001 * TIMESCALE;
586 
587         tick(delta);
588 
589         for (std::list<PSubsystem *>::iterator i = sslist.begin();
590           i != sslist.end(); ++i) {
591           (*i)->tick(delta, cam_pos, cam_orimat, cam_linvel);
592         }
593       }
594     }
595 
596     curtime = nowtime;
597 
598     if (exit_requested) break;
599 
600     if (active || repaint) {
601       switch (stereo) {
602 
603       case StereoNone: // Normal, non-stereo rendering
604 
605         render(0.0f);
606         glFlush();
607         SDL_GL_SwapWindow(screen);
608         break;
609 
610       case StereoQuadBuffer: // Hardware quad buffer stereo
611 
612         glDrawBuffer(GL_BACK_LEFT);
613         render(-stereoEyeTranslation);
614         glFlush();
615 
616         glDrawBuffer(GL_BACK_RIGHT);
617         render(stereoEyeTranslation);
618         glFlush();
619 
620         SDL_GL_SwapWindow(screen);
621         break;
622 
623       case StereoRedBlue: // Red-blue anaglyph stereo
624 
625         // Green will not be rendered to, so clear it
626         glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
627         glClearColor(0.5, 0.5, 0.5, 0.5);
628         glClear(GL_COLOR_BUFFER_BIT);
629 
630         glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
631         render(-stereoEyeTranslation);
632         glFlush();
633 
634         glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);
635         render(stereoEyeTranslation);
636         glFlush();
637 
638         SDL_GL_SwapWindow(screen);
639         break;
640 
641       case StereoRedGreen: // Red-green anaglyph stereo
642 
643         // Blue will not be rendered to, so clear it
644         glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);
645         glClearColor(0.5, 0.5, 0.5, 0.5);
646         glClear(GL_COLOR_BUFFER_BIT);
647 
648         glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
649         render(-stereoEyeTranslation);
650         glFlush();
651 
652         glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
653         render(stereoEyeTranslation);
654         glFlush();
655 
656         SDL_GL_SwapWindow(screen);
657         break;
658 
659       case StereoRedCyan: // Red-cyan anaglyph stereo
660 
661         glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
662         render(-stereoEyeTranslation);
663         glFlush();
664 
665         glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
666         render(stereoEyeTranslation);
667         glFlush();
668 
669         SDL_GL_SwapWindow(screen);
670         break;
671 
672       case StereoYellowBlue: // Yellow-blue anaglyph stereo
673 
674         glColorMask(GL_TRUE, GL_TRUE, GL_FALSE, GL_TRUE);
675         render(-stereoEyeTranslation);
676         glFlush();
677 
678         glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);
679         render(stereoEyeTranslation);
680         glFlush();
681 
682         SDL_GL_SwapWindow(screen);
683         break;
684       }
685       repaint = false;
686 
687       if (screenshot_requested) {
688         glReadBuffer(GL_FRONT);
689         unsigned char *data1 = new unsigned char[cx*(cy+1)*3];
690         glReadPixels(0, 0, cx, cy, GL_RGB, GL_UNSIGNED_BYTE, data1);
691         glReadBuffer(GL_BACK);
692         const int rowsize = cx * 3;
693         for(int i = 0; i < cy/2; ++i) {
694           memcpy(&data1[(cy) * rowsize], &data1[(cy-1-i) * rowsize], rowsize);
695           memcpy(&data1[(cy-1-i) * rowsize], &data1[(i) * rowsize], rowsize);
696           memcpy(&data1[(i) * rowsize], &data1[(cy) * rowsize], rowsize);
697           //memset(&data1[(i) * rowsize], 128, rowsize);
698         }
699         char buff[200];
700         sprintf(buff, "P6\n"
701           "# CREATOR: Trigger PNM Screenshot\n"
702           "%i %i\n255\n", cx, cy);
703         char filename[100];
704         sprintf(filename, "screen-%09u.ppm", SDL_GetTicks());
705         PUtil::outLog() << "Writing screenshot \"" << filename << "\"" << std::endl;
706         PHYSFS_file* pfile = PHYSFS_openWrite(filename);
707         if (pfile) {
708           physfs_write(pfile, buff, sizeof(char), strlen(buff));
709           physfs_write(pfile, data1, sizeof(char), cx*cy*3);
710           PHYSFS_close(pfile);
711         } else {
712           PUtil::outLog() << "Screenshot write failed" << std::endl;
713           PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
714         }
715         delete[] data1;
716         screenshot_requested = false;
717       }
718 
719     } else {
720       SDL_WaitEvent(nullptr);
721     }
722 
723     if (exit_requested) break;
724   }
725 
726   PUtil::outLog() << "Exit requested" << std::endl;
727 
728   unload();
729 
730   while (!sslist.empty()) {
731     delete sslist.back();
732     sslist.pop_back();
733   }
734 
735   //SDL_WM_GrabInput(SDL_GRAB_OFF);
736   //SDL_SetWindowGrab(screen, SDL_FALSE);
737   SDL_ShowCursor(SDL_ENABLE);
738 
739     SDL_GL_DeleteContext(context);
740     SDL_DestroyWindow(screen);
741     SDL_Quit();
742 
743   best_times.savePlayer();
744 
745   if (PHYSFS_deinit() == 0) {
746     PUtil::outLog() << "PhysFS: " << physfs_getErrorString() << std::endl;
747   }
748 
749   PUtil::outLog() << "Shutdown complete" << std::endl;
750 
751   return 0;
752 }
753 
grabMouse(bool grab)754 void PApp::grabMouse(bool grab)
755 {
756   //SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF);
757   SDL_ShowCursor(grab ? SDL_DISABLE : SDL_ENABLE);
758 
759   grabinput = grab;
760 }
761 
drawModel(PModel & model)762 void PApp::drawModel(PModel &model)
763 {
764   for (std::vector<PMesh>::iterator mesh = model.mesh.begin();
765     mesh != model.mesh.end();
766     ++mesh) {
767     if (!mesh->effect)
768       mesh->effect = getSSEffect().loadEffect(mesh->fxname);
769 
770     int numPasses = 0;
771     if (mesh->effect->renderBegin(&numPasses, getSSTexture())) {
772       for (int i=0; i<numPasses; i++) {
773         mesh->effect->renderPass(i);
774         glBegin(GL_TRIANGLES);
775         for (unsigned int f=0; f<mesh->face.size(); f++) {
776           //glNormal3fv(mesh->face[f].facenormal);
777 
778           glNormal3fv(mesh->norm[mesh->face[f].nr[0]]);
779           glTexCoord2fv(mesh->texco[mesh->face[f].tc[0]]);
780           glVertex3fv(mesh->vert[mesh->face[f].vt[0]]);
781 
782           glNormal3fv(mesh->norm[mesh->face[f].nr[1]]);
783           glTexCoord2fv(mesh->texco[mesh->face[f].tc[1]]);
784           glVertex3fv(mesh->vert[mesh->face[f].vt[1]]);
785 
786           glNormal3fv(mesh->norm[mesh->face[f].nr[2]]);
787           glTexCoord2fv(mesh->texco[mesh->face[f].tc[2]]);
788           glVertex3fv(mesh->vert[mesh->face[f].vt[2]]);
789         }
790         glEnd();
791       }
792       mesh->effect->renderEnd();
793     }
794   }
795 }
796 
797 
798 // default callback functions
799 
config()800 void PApp::config()
801 {
802 }
803 
load()804 void PApp::load()
805 {
806 }
807 
unload()808 void PApp::unload()
809 {
810 }
811 
tick(float delta)812 void PApp::tick(float delta)
813 {
814   delta = delta;
815 }
816 
resize()817 void PApp::resize()
818 {
819 }
820 
render(float eyetranslation)821 void PApp::render(float eyetranslation)
822 {
823   eyetranslation = eyetranslation;
824 
825   glClearColor(0.5, 0.5, 0.5, 0.0);
826 
827   glClear(GL_COLOR_BUFFER_BIT);
828 }
829 
keyEvent(const SDL_KeyboardEvent & ke)830 void PApp::keyEvent(const SDL_KeyboardEvent &ke)
831 {
832   if (ke.type != SDL_KEYDOWN) return;
833 
834   switch (ke.keysym.sym) {
835   case SDLK_ESCAPE:
836     requestExit();
837     break;
838   default:
839     break;
840   }
841 }
842 
mouseButtonEvent(const SDL_MouseButtonEvent & mbe)843 void PApp::mouseButtonEvent(const SDL_MouseButtonEvent &mbe)
844 {
845   int unused = mbe.type; unused = unused;
846 }
847 
mouseMoveEvent(int dx,int dy)848 void PApp::mouseMoveEvent(int dx, int dy)
849 {
850   dx = dx; dy = dy;
851 }
852 
cursorMoveEvent(int posx,int posy)853 void PApp::cursorMoveEvent(int posx, int posy)
854 {
855   posx = posx; posy = posy;
856 }
857 
joyButtonEvent(int which,int button,bool down)858 void PApp::joyButtonEvent(int which, int button, bool down)
859 {
860   which = which; button = button; down = down;
861 }
862 
863 
864