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