]> git.jsancho.org Git - lugaru.git/blob - Source/OpenGL_Windows.cpp
Removed commented out code
[lugaru.git] / Source / OpenGL_Windows.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3
4 This file is part of Lugaru.
5
6 Lugaru is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Lugaru is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21 #ifdef WIN32
22 #include <windows.h>
23 #endif
24
25 #include "Game.h"
26
27 using namespace Game;
28
29 #include "openal_wrapper.h"
30
31 extern float multiplier;
32 extern float sps;
33 extern float realmultiplier;
34 extern int slomo;
35 extern bool cellophane;
36 extern float texdetail;
37
38 extern bool osx;
39 extern bool freeze;
40 extern bool stillloading;
41 extern int mainmenu;
42
43 extern float slomospeed;
44 extern float slomofreq;
45 extern bool visibleloading;
46
47 #include <math.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <fstream>
51 #include <iostream>
52 #include <zlib.h>
53 #include <set>
54 #include "gamegl.h"
55 #include "MacCompatibility.h"
56 #include "Settings.h"
57
58 #ifdef WIN32
59 #include <shellapi.h>
60 #include "win-res/resource.h"
61 #endif
62
63 extern SDL_Window *sdlwindow;
64
65 using namespace std;
66
67 set<pair<int,int>> resolutions;
68
69 bool SetUp ();
70 void DoUpdate ();
71
72 void CleanUp (void);
73
74 // statics/globals (internal only) ------------------------------------------
75
76 #ifdef _MSC_VER
77 #pragma warning(push)
78 #pragma warning(disable: 4273)
79 #endif
80
81 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
82 #define GL_FUNC(ret,fn,params,call,rt) \
83     extern "C" { \
84         static ret (GLAPIENTRY *p##fn) params = NULL; \
85         ret GLAPIENTRY fn params { rt p##fn call; } \
86     }
87 #include "glstubs.h"
88 #undef GL_FUNC
89 #endif // __MINGW32__
90
91 #ifdef _MSC_VER
92 #pragma warning(pop)
93 #endif
94
95 static bool lookup_glsym(const char *funcname, void **func)
96 {
97     *func = SDL_GL_GetProcAddress(funcname);
98     if (*func == NULL) {
99         fprintf(stderr, "Failed to find OpenGL symbol \"%s\"\n", funcname);
100         return false;
101     }
102     return true;
103 }
104
105 static bool lookup_all_glsyms(void)
106 {
107     bool retval = true;
108 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
109 #define GL_FUNC(ret,fn,params,call,rt) \
110         if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
111 #include "glstubs.h"
112 #undef GL_FUNC
113 #endif // __MINGW32__
114     return retval;
115 }
116
117 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
118 static void GLAPIENTRY glDeleteTextures_doNothing(GLsizei n, const GLuint *textures)
119 {
120     // no-op.
121 }
122 #endif // __MINGW32__
123
124 #ifdef MessageBox
125 #undef MessageBox
126 #endif
127 #define MessageBox(hwnd,text,title,flags) STUBBED("msgbox")
128
129 // Menu defs
130
131 int kContextWidth;
132 int kContextHeight;
133
134 bool gDone = false;
135 bool gameFocused;
136
137 static int _argc = 0;
138 static char **_argv = NULL;
139
140 bool cmdline(const char *cmd)
141 {
142     for (int i = 1; i < _argc; i++) {
143         char *arg = _argv[i];
144         while (*arg == '-')
145             arg++;
146         if (strcasecmp(arg, cmd) == 0)
147             return true;
148     }
149
150     return false;
151 }
152
153 //-----------------------------------------------------------------------------------------------------------------------
154
155 // OpenGL Drawing
156
157 void initGL()
158 {
159     glClear( GL_COLOR_BUFFER_BIT );
160     swap_gl_buffers();
161
162     // clear all states
163     glDisable( GL_ALPHA_TEST);
164     glDisable( GL_BLEND);
165     glDisable( GL_DEPTH_TEST);
166     glDisable( GL_FOG);
167     glDisable( GL_LIGHTING);
168     glDisable( GL_LOGIC_OP);
169     glDisable( GL_TEXTURE_1D);
170     glDisable( GL_TEXTURE_2D);
171     glPixelTransferi( GL_MAP_COLOR, GL_FALSE);
172     glPixelTransferi( GL_RED_SCALE, 1);
173     glPixelTransferi( GL_RED_BIAS, 0);
174     glPixelTransferi( GL_GREEN_SCALE, 1);
175     glPixelTransferi( GL_GREEN_BIAS, 0);
176     glPixelTransferi( GL_BLUE_SCALE, 1);
177     glPixelTransferi( GL_BLUE_BIAS, 0);
178     glPixelTransferi( GL_ALPHA_SCALE, 1);
179     glPixelTransferi( GL_ALPHA_BIAS, 0);
180
181     // set initial rendering states
182     glShadeModel( GL_SMOOTH);
183     glClearDepth( 1.0f);
184     glDepthFunc( GL_LEQUAL);
185     glDepthMask( GL_TRUE);
186     glEnable( GL_DEPTH_TEST);
187     glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
188     glCullFace( GL_FRONT);
189     glEnable( GL_CULL_FACE);
190     glEnable( GL_LIGHTING);
191     glEnable( GL_DITHER);
192     glEnable( GL_COLOR_MATERIAL);
193     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
194     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
195     glAlphaFunc( GL_GREATER, 0.5f);
196
197     if ( CanInitStereo(stereomode) ) {
198         InitStereo(stereomode);
199     } else {
200         fprintf(stderr, "Failed to initialize stereo, disabling.\n");
201         stereomode = stereoNone;
202     }
203 }
204
205 void toggleFullscreen()
206 {
207     fullscreen = !fullscreen;
208     Uint32 flags = SDL_GetWindowFlags(sdlwindow);
209     if (flags & SDL_WINDOW_FULLSCREEN) {
210         flags &= ~SDL_WINDOW_FULLSCREEN;
211     } else {
212         flags |= SDL_WINDOW_FULLSCREEN;
213     }
214     SDL_SetWindowFullscreen(sdlwindow, flags);
215 }
216
217 static SDL_bool sdlEventProc(const SDL_Event &e)
218 {
219     switch (e.type) {
220         case SDL_QUIT:
221             return SDL_FALSE;
222
223         case SDL_WINDOWEVENT:
224             if (e.window.event == SDL_WINDOWEVENT_CLOSE) {
225                 return SDL_FALSE;
226             }
227         break;
228
229         case SDL_MOUSEMOTION:
230             deltah += e.motion.xrel;
231             deltav += e.motion.yrel;
232         break;
233
234         case SDL_KEYDOWN:
235             if ((e.key.keysym.scancode == SDL_SCANCODE_G) &&
236                 (e.key.keysym.mod & KMOD_CTRL)) {
237                 SDL_bool mode = SDL_TRUE;
238                 if ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_FULLSCREEN) == 0)
239                     mode = (SDL_GetWindowGrab(sdlwindow) ? SDL_FALSE : SDL_TRUE);
240                 SDL_SetWindowGrab(sdlwindow, mode);
241                 SDL_SetRelativeMouseMode(mode);
242             } else if ( (e.key.keysym.scancode == SDL_SCANCODE_RETURN) && (e.key.keysym.mod & KMOD_ALT) ) {
243                 toggleFullscreen();
244             }
245         break;
246     }
247     return SDL_TRUE;
248 }
249
250
251
252 // --------------------------------------------------------------------------
253
254 static Point gMidPoint;
255
256 bool SetUp ()
257 {
258     LOGFUNC;
259
260     osx = 0;
261     cellophane = 0;
262     texdetail = 4;
263     slomospeed = 0.25;
264     slomofreq = 8012;
265
266     DefaultSettings();
267
268     if (!SDL_WasInit(SDL_INIT_VIDEO))
269         if (SDL_Init(SDL_INIT_VIDEO) == -1) {
270             fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
271             return false;
272         }
273     if (!LoadSettings()) {
274         fprintf(stderr, "Failed to load config, creating default\n");
275         SaveSettings();
276     }
277     if (kBitsPerPixel != 32 && kBitsPerPixel != 16) {
278         kBitsPerPixel = 16;
279     }
280
281     if (SDL_GL_LoadLibrary(NULL) == -1) {
282         fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
283         SDL_Quit();
284         return false;
285     }
286
287     for (int displayIdx = 0; displayIdx < SDL_GetNumVideoDisplays(); ++displayIdx) {
288         for (int i = 0; i < SDL_GetNumDisplayModes(displayIdx); ++i) {
289             SDL_DisplayMode mode;
290             if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
291                 continue;
292             if ((mode.w < 640) || (mode.h < 480))
293                 continue;  // sane lower limit.
294             pair<int,int> resolution(mode.w, mode.h);
295             resolutions.insert(resolution);
296         }
297     }
298
299     if (resolutions.empty()) {
300         const std::string error = "No suitable video resolutions found.";
301         cerr << error << endl;
302         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Lugaru init failed!", error.c_str(), NULL);
303         SDL_Quit();
304         return false;
305     }
306
307     if (cmdline("showresolutions")) {
308         printf("Available resolutions:\n");
309         for (auto resolution = resolutions.begin(); resolution != resolutions.end(); resolution++) {
310             printf("  %d x %d\n", (int) resolution->first, (int) resolution->second);
311         }
312     }
313
314     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
315     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
316
317     Uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
318     if ((fullscreen || cmdline("fullscreen")) && !cmdline("windowed")) {
319         fullscreen = 1;
320         sdlflags |= SDL_WINDOW_FULLSCREEN;
321     }
322     if (!cmdline("nomousegrab"))
323         sdlflags |= SDL_WINDOW_INPUT_GRABBED;
324
325     sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
326                                  kContextWidth, kContextHeight, sdlflags);
327
328     if (!sdlwindow) {
329         fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
330         fprintf(stderr, "forcing 640x480...\n");
331         kContextWidth = 640;
332         kContextHeight = 480;
333         sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
334                                      kContextWidth, kContextHeight, sdlflags);
335         if (!sdlwindow) {
336             fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
337             fprintf(stderr, "forcing 640x480 windowed mode...\n");
338             sdlflags &= ~SDL_WINDOW_FULLSCREEN;
339             sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
340                                          kContextWidth, kContextHeight, sdlflags);
341
342             if (!sdlwindow) {
343                 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
344                 return false;
345             }
346         }
347     }
348
349     SDL_GLContext glctx = SDL_GL_CreateContext(sdlwindow);
350     if (!glctx) {
351         fprintf(stderr, "SDL_GL_CreateContext() failed: %s\n", SDL_GetError());
352         SDL_Quit();
353         return false;
354     }
355
356     SDL_GL_MakeCurrent(sdlwindow, glctx);
357
358     if (!lookup_all_glsyms()) {
359         fprintf(stderr, "Missing required OpenGL functions.\n");
360         SDL_Quit();
361         return false;
362     }
363
364     int dblbuf = 0;
365     if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
366     {
367         fprintf(stderr, "Failed to get a double-buffered context.\n");
368         SDL_Quit();
369         return false;
370     }
371
372     if (SDL_GL_SetSwapInterval(-1) == -1)  // try swap_tear first.
373         SDL_GL_SetSwapInterval(1);
374
375     SDL_ShowCursor(0);
376     if (!cmdline("nomousegrab"))
377         SDL_SetRelativeMouseMode(SDL_TRUE);
378
379     initGL();
380
381     GLint width = kContextWidth;
382     GLint height = kContextHeight;
383     gMidPoint.h = width / 2;
384     gMidPoint.v = height / 2;
385     screenwidth = width;
386     screenheight = height;
387
388     newdetail = detail;
389     newscreenwidth = screenwidth;
390     newscreenheight = screenheight;
391
392     /* If saved resolution is not in the list, add it to the list (so that it’s selectable in the options) */
393     pair<int,int> startresolution(width,height);
394     if (resolutions.find(startresolution) == resolutions.end()) {
395         resolutions.insert(startresolution);
396     }
397
398     InitGame();
399
400     return true;
401 }
402
403
404 static void DoMouse()
405 {
406
407     if (mainmenu || ( (abs(deltah) < 10 * realmultiplier * 1000) && (abs(deltav) < 10 * realmultiplier * 1000) )) {
408         deltah *= usermousesensitivity;
409         deltav *= usermousesensitivity;
410         mousecoordh += deltah;
411         mousecoordv += deltav;
412         if (mousecoordh < 0)
413             mousecoordh = 0;
414         else if (mousecoordh >= kContextWidth)
415             mousecoordh = kContextWidth - 1;
416         if (mousecoordv < 0)
417             mousecoordv = 0;
418         else if (mousecoordv >= kContextHeight)
419             mousecoordv = kContextHeight - 1;
420     }
421
422 }
423
424 void DoFrameRate (int update)
425 {
426     static long frames = 0;
427
428     static AbsoluteTime time = {0, 0};
429     static AbsoluteTime frametime = {0, 0};
430     AbsoluteTime currTime = UpTime ();
431     double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
432
433     if (0 > deltaTime) // if negative microseconds
434         deltaTime /= -1000000.0;
435     else // else milliseconds
436         deltaTime /= 1000.0;
437
438     multiplier = deltaTime;
439     if (multiplier < .001)
440         multiplier = .001;
441     if (multiplier > 10)
442         multiplier = 10;
443     if (update)
444         frametime = currTime; // reset for next time interval
445
446     deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
447
448     if (0 > deltaTime) // if negative microseconds
449         deltaTime /= -1000000.0;
450     else // else milliseconds
451         deltaTime /= 1000.0;
452     frames++;
453     if (0.001 <= deltaTime) { // has update interval passed
454         if (update) {
455             time = currTime; // reset for next time interval
456             frames = 0;
457         }
458     }
459 }
460
461
462 void DoUpdate ()
463 {
464     static float sps = 200;
465     static int count;
466     static float oldmult;
467
468     DoFrameRate(1);
469     if (multiplier > .6)
470         multiplier = .6;
471
472     fps = 1 / multiplier;
473
474     count = multiplier * sps;
475     if (count < 2)
476         count = 2;
477
478     realmultiplier = multiplier;
479     multiplier *= gamespeed;
480     if (difficulty == 1)
481         multiplier *= .9;
482     if (difficulty == 0)
483         multiplier *= .8;
484
485     if (loading == 4)
486         multiplier *= .00001;
487     if (slomo && !mainmenu)
488         multiplier *= slomospeed;
489     oldmult = multiplier;
490     multiplier /= (float)count;
491
492     DoMouse();
493
494     TickOnce();
495
496     for (int i = 0; i < count; i++) {
497         Tick();
498     }
499     multiplier = oldmult;
500
501     TickOnceAfter();
502     /* - Debug code to test how many channels were active on average per frame
503         static long frames = 0;
504
505         static AbsoluteTime start = {0,0};
506         AbsoluteTime currTime = UpTime ();
507         static int num_channels = 0;
508
509         num_channels += OPENAL_GetChannelsPlaying();
510         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
511
512         if (0 > deltaTime)  // if negative microseconds
513             deltaTime /= -1000000.0;
514         else                // else milliseconds
515             deltaTime /= 1000.0;
516
517         ++frames;
518
519         if (deltaTime >= 1)
520         {
521             start = currTime;
522             float avg_channels = (float)num_channels / (float)frames;
523
524             ofstream opstream("log.txt",ios::app);
525             opstream << "Average frame count: ";
526             opstream << frames;
527             opstream << " frames - ";
528             opstream << avg_channels;
529             opstream << " per frame.\n";
530             opstream.close();
531
532             frames = 0;
533             num_channels = 0;
534         }
535     */
536     if ( stereomode == stereoNone ) {
537         DrawGLScene(stereoCenter);
538     } else {
539         DrawGLScene(stereoLeft);
540         DrawGLScene(stereoRight);
541     }
542 }
543
544 // --------------------------------------------------------------------------
545
546
547 void CleanUp (void)
548 {
549     LOGFUNC;
550
551     SDL_Quit();
552 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
553 #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
554 #include "glstubs.h"
555 #undef GL_FUNC
556     // cheat here...static destructors are calling glDeleteTexture() after
557     //  the context is destroyed and libGL unloaded by SDL_Quit().
558     pglDeleteTextures = glDeleteTextures_doNothing;
559 #endif // __MINGW32__
560
561 }
562
563 // --------------------------------------------------------------------------
564
565 static bool IsFocused()
566 {
567     return ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_INPUT_FOCUS) != 0);
568 }
569
570
571
572 #ifndef WIN32
573 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
574 static char *findBinaryInPath(const char *bin, char *envr)
575 {
576     size_t alloc_size = 0;
577     char *exe = NULL;
578     char *start = envr;
579     char *ptr;
580
581     do {
582         size_t size;
583         ptr = strchr(start, ':');  /* find next $PATH separator. */
584         if (ptr)
585             *ptr = '\0';
586
587         size = strlen(start) + strlen(bin) + 2;
588         if (size > alloc_size) {
589             char *x = (char *) realloc(exe, size);
590             if (x == NULL) {
591                 if (exe != NULL)
592                     free(exe);
593                 return(NULL);
594             } /* if */
595
596             alloc_size = size;
597             exe = x;
598         } /* if */
599
600         /* build full binary path... */
601         strcpy(exe, start);
602         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
603             strcat(exe, "/");
604         strcat(exe, bin);
605
606         if (access(exe, X_OK) == 0) { /* Exists as executable? We're done. */
607             strcpy(exe, start);  /* i'm lazy. piss off. */
608             return(exe);
609         } /* if */
610
611         start = ptr + 1;  /* start points to beginning of next element. */
612     } while (ptr != NULL);
613
614     if (exe != NULL)
615         free(exe);
616
617     return(NULL);  /* doesn't exist in path. */
618 } /* findBinaryInPath */
619
620
621 char *calcBaseDir(const char *argv0)
622 {
623     /* If there isn't a path on argv0, then look through the $PATH for it. */
624     char *retval;
625     char *envr;
626
627     if (strchr(argv0, '/')) {
628         retval = strdup(argv0);
629         if (retval)
630             *((char *) strrchr(retval, '/')) = '\0';
631         return(retval);
632     }
633
634     envr = getenv("PATH");
635     if (!envr)
636         return NULL;
637     envr = strdup(envr);
638     if (!envr)
639         return NULL;
640     retval = findBinaryInPath(argv0, envr);
641     free(envr);
642     return(retval);
643 }
644
645 static inline void chdirToAppPath(const char *argv0)
646 {
647     char *dir = calcBaseDir(argv0);
648     if (dir) {
649 #if (defined(__APPLE__) && defined(__MACH__))
650         // Chop off /Contents/MacOS if it's at the end of the string, so we
651         //  land in the base of the app bundle.
652         const size_t len = strlen(dir);
653         const char *bundledirs = "/Contents/MacOS";
654         const size_t bundledirslen = strlen(bundledirs);
655         if (len > bundledirslen) {
656             char *ptr = (dir + len) - bundledirslen;
657             if (strcasecmp(ptr, bundledirs) == 0)
658                 *ptr = '\0';
659         }
660 #endif
661         chdir(dir);
662         free(dir);
663     }
664 }
665 #endif
666
667
668 int main(int argc, char **argv)
669 {
670     _argc = argc;
671     _argv = argv;
672
673     // !!! FIXME: we could use a Win32 API for this.  --ryan.
674 #ifndef WIN32
675     chdirToAppPath(argv[0]);
676 #endif
677
678     LOGFUNC;
679
680     try {
681         {
682             newGame();
683
684             if (!SetUp ())
685                 return 42;
686
687             while (!gDone && !tryquit) {
688                 if (IsFocused()) {
689                     gameFocused = true;
690
691                     // check windows messages
692
693                     deltah = 0;
694                     deltav = 0;
695                     SDL_Event e;
696                     if (!waiting) {
697                         // message pump
698                         while ( SDL_PollEvent( &e ) ) {
699                             if (!sdlEventProc(e)) {
700                                 gDone = true;
701                                 break;
702                             }
703                         }
704                     }
705
706                     // game
707                     DoUpdate();
708                 } else {
709                     if (gameFocused) {
710                         // allow game chance to pause
711                         gameFocused = false;
712                         DoUpdate();
713                     }
714
715                     // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
716                     SDL_WaitEvent(0);
717                 }
718             }
719
720             deleteGame();
721         }
722
723         CleanUp ();
724
725         return 0;
726     } catch (const std::exception& error) {
727         CleanUp();
728
729         std::string e = "Caught exception: ";
730         e += error.what();
731
732         LOG(e);
733
734         MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
735     }
736
737     CleanUp();
738
739     return -1;
740 }