2 Copyright (C) 2003, 2010 - Wolfire Games
4 This file is part of Lugaru.
6 Lugaru is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program 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.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35 static bool load_image(const char * fname, TGAImageRec & tex);
36 static bool load_png(const char * fname, TGAImageRec & tex);
37 static bool load_jpg(const char * fname, TGAImageRec & tex);
38 bool save_image(const char * fname);
39 static bool save_png(const char * fname);
42 #include "openal_wrapper.h"
44 extern float multiplier;
46 extern float realmultiplier;
48 extern bool cellophane;
49 extern float texdetail;
53 extern bool stillloading;
58 extern float slomospeed;
59 extern float slomofreq;
70 #include "MacCompatibility.h"
75 #include "win-res/resource.h"
78 extern SDL_Window *sdlwindow;
82 set<pair<int,int>> resolutions;
89 // statics/globals (internal only) ------------------------------------------
93 #pragma warning(disable: 4273)
96 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
97 #define GL_FUNC(ret,fn,params,call,rt) \
99 static ret (GLAPIENTRY *p##fn) params = NULL; \
100 ret GLAPIENTRY fn params { rt p##fn call; } \
104 #endif // __MINGW32__
110 static bool lookup_glsym(const char *funcname, void **func)
112 *func = SDL_GL_GetProcAddress(funcname);
114 fprintf(stderr, "Failed to find OpenGL symbol \"%s\"\n", funcname);
120 static bool lookup_all_glsyms(void)
123 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
124 #define GL_FUNC(ret,fn,params,call,rt) \
125 if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
128 #endif // __MINGW32__
132 static void GLAPIENTRY glDeleteTextures_doNothing(GLsizei n, const GLuint *textures)
140 #define MessageBox(hwnd,text,title,flags) STUBBED("msgbox")
147 Boolean gDone = false;
149 static int _argc = 0;
150 static char **_argv = NULL;
152 bool cmdline(const char *cmd)
154 for (int i = 1; i < _argc; i++) {
155 char *arg = _argv[i];
158 if (strcasecmp(arg, cmd) == 0)
165 //-----------------------------------------------------------------------------------------------------------------------
171 glClear( GL_COLOR_BUFFER_BIT );
175 glDisable( GL_ALPHA_TEST);
176 glDisable( GL_BLEND);
177 glDisable( GL_DEPTH_TEST);
178 //glDisable( GL_DITHER);
180 glDisable( GL_LIGHTING);
181 glDisable( GL_LOGIC_OP);
182 glDisable( GL_TEXTURE_1D);
183 glDisable( GL_TEXTURE_2D);
184 glPixelTransferi( GL_MAP_COLOR, GL_FALSE);
185 glPixelTransferi( GL_RED_SCALE, 1);
186 glPixelTransferi( GL_RED_BIAS, 0);
187 glPixelTransferi( GL_GREEN_SCALE, 1);
188 glPixelTransferi( GL_GREEN_BIAS, 0);
189 glPixelTransferi( GL_BLUE_SCALE, 1);
190 glPixelTransferi( GL_BLUE_BIAS, 0);
191 glPixelTransferi( GL_ALPHA_SCALE, 1);
192 glPixelTransferi( GL_ALPHA_BIAS, 0);
194 // set initial rendering states
195 glShadeModel( GL_SMOOTH);
197 glDepthFunc( GL_LEQUAL);
198 glDepthMask( GL_TRUE);
199 //glDepthRange( FRONT_CLIP, BACK_CLIP);
200 glEnable( GL_DEPTH_TEST);
201 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
202 glCullFace( GL_FRONT);
203 glEnable( GL_CULL_FACE);
204 glEnable( GL_LIGHTING);
205 //glEnable( GL_LIGHT_MODEL_AMBIENT);
206 glEnable( GL_DITHER);
207 glEnable( GL_COLOR_MATERIAL);
208 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
209 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
210 glAlphaFunc( GL_GREATER, 0.5f);
212 if ( CanInitStereo(stereomode) ) {
213 InitStereo(stereomode);
215 fprintf(stderr, "Failed to initialize stereo, disabling.\n");
216 stereomode = stereoNone;
220 static void toggleFullscreen()
222 Uint32 flags = SDL_GetWindowFlags(sdlwindow);
223 if (flags & SDL_WINDOW_FULLSCREEN) {
224 flags &= ~SDL_WINDOW_FULLSCREEN;
226 flags |= SDL_WINDOW_FULLSCREEN;
228 SDL_SetWindowFullscreen(sdlwindow, flags);
231 static SDL_bool sdlEventProc(const SDL_Event &e)
237 case SDL_WINDOWEVENT:
238 if (e.window.event == SDL_WINDOWEVENT_CLOSE) {
243 case SDL_MOUSEMOTION:
244 deltah += e.motion.xrel;
245 deltav += e.motion.yrel;
249 if ((e.key.keysym.scancode == SDL_SCANCODE_G) &&
250 (e.key.keysym.mod & KMOD_CTRL)) {
251 SDL_bool mode = SDL_TRUE;
252 if ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_FULLSCREEN) == 0)
253 mode = (SDL_GetWindowGrab(sdlwindow) ? SDL_FALSE : SDL_TRUE);
254 SDL_SetWindowGrab(sdlwindow, mode);
255 SDL_SetRelativeMouseMode(mode);
256 } else if ( (e.key.keysym.scancode == SDL_SCANCODE_RETURN) && (e.key.keysym.mod & KMOD_ALT) ) {
266 // --------------------------------------------------------------------------
268 static Point gMidPoint;
284 if (!SDL_WasInit(SDL_INIT_VIDEO))
285 if (SDL_Init(SDL_INIT_VIDEO) == -1) {
286 fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
289 if (!LoadSettings()) {
290 fprintf(stderr, "Failed to load config, creating default\n");
293 if (kBitsPerPixel != 32 && kBitsPerPixel != 16) {
297 if (SDL_GL_LoadLibrary(NULL) == -1) {
298 fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
303 for (int displayIdx = 0; displayIdx < SDL_GetNumVideoDisplays(); ++displayIdx) {
304 for (int i = 0; i < SDL_GetNumDisplayModes(displayIdx); ++i) {
305 SDL_DisplayMode mode;
306 if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
308 if ((mode.w < 640) || (mode.h < 480))
309 continue; // sane lower limit.
310 pair<int,int> resolution(mode.w, mode.h);
311 resolutions.insert(resolution);
315 if (resolutions.empty()) {
316 const std::string error = "No suitable video resolutions found.";
317 cerr << error << endl;
318 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Lugaru init failed!", error.c_str(), NULL);
323 if (cmdline("showresolutions")) {
324 printf("Available resolutions:\n");
325 for (auto resolution = resolutions.begin(); resolution != resolutions.end(); resolution++) {
326 printf(" %d x %d\n", (int) resolution->first, (int) resolution->second);
330 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
331 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
333 Uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
334 if (!cmdline("windowed"))
335 sdlflags |= SDL_WINDOW_FULLSCREEN;
336 if (!cmdline("nomousegrab"))
337 sdlflags |= SDL_WINDOW_INPUT_GRABBED;
339 sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
340 kContextWidth, kContextHeight, sdlflags);
343 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
344 fprintf(stderr, "forcing 640x480...\n");
346 kContextHeight = 480;
347 sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
348 kContextWidth, kContextHeight, sdlflags);
350 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
351 fprintf(stderr, "forcing 640x480 windowed mode...\n");
352 sdlflags &= ~SDL_WINDOW_FULLSCREEN;
353 sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
354 kContextWidth, kContextHeight, sdlflags);
357 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
363 SDL_GLContext glctx = SDL_GL_CreateContext(sdlwindow);
365 fprintf(stderr, "SDL_GL_CreateContext() failed: %s\n", SDL_GetError());
370 SDL_GL_MakeCurrent(sdlwindow, glctx);
372 if (!lookup_all_glsyms()) {
373 fprintf(stderr, "Missing required OpenGL functions.\n");
379 if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
381 fprintf(stderr, "Failed to get a double-buffered context.\n");
386 if (SDL_GL_SetSwapInterval(-1) == -1) // try swap_tear first.
387 SDL_GL_SetSwapInterval(1);
390 if (!cmdline("nomousegrab"))
391 SDL_SetRelativeMouseMode(SDL_TRUE);
395 GLint width = kContextWidth;
396 GLint height = kContextHeight;
397 gMidPoint.h = width / 2;
398 gMidPoint.v = height / 2;
400 screenheight = height;
403 newscreenwidth = screenwidth;
404 newscreenheight = screenheight;
406 /* If saved resolution is not in the list, add it to the list (so that it’s selectable in the options) */
407 pair<int,int> startresolution(width,height);
408 if (resolutions.find(startresolution) == resolutions.end()) {
409 resolutions.insert(startresolution);
418 static void DoMouse()
421 if (mainmenu || ( (abs(deltah) < 10 * realmultiplier * 1000) && (abs(deltav) < 10 * realmultiplier * 1000) )) {
422 deltah *= usermousesensitivity;
423 deltav *= usermousesensitivity;
424 mousecoordh += deltah;
425 mousecoordv += deltav;
428 else if (mousecoordh >= kContextWidth)
429 mousecoordh = kContextWidth - 1;
432 else if (mousecoordv >= kContextHeight)
433 mousecoordv = kContextHeight - 1;
438 void DoFrameRate (int update)
440 static long frames = 0;
442 static AbsoluteTime time = {0, 0};
443 static AbsoluteTime frametime = {0, 0};
444 AbsoluteTime currTime = UpTime ();
445 double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
447 if (0 > deltaTime) // if negative microseconds
448 deltaTime /= -1000000.0;
449 else // else milliseconds
452 multiplier = deltaTime;
453 if (multiplier < .001)
458 frametime = currTime; // reset for next time interval
460 deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
462 if (0 > deltaTime) // if negative microseconds
463 deltaTime /= -1000000.0;
464 else // else milliseconds
467 if (0.001 <= deltaTime) { // has update interval passed
469 time = currTime; // reset for next time interval
478 static float sps = 200;
480 static float oldmult;
486 fps = 1 / multiplier;
488 count = multiplier * sps;
492 realmultiplier = multiplier;
493 multiplier *= gamespeed;
500 multiplier *= .00001;
501 if (slomo && !mainmenu)
502 multiplier *= slomospeed;
503 oldmult = multiplier;
504 multiplier /= (float)count;
510 for (int i = 0; i < count; i++) {
513 multiplier = oldmult;
516 /* - Debug code to test how many channels were active on average per frame
517 static long frames = 0;
519 static AbsoluteTime start = {0,0};
520 AbsoluteTime currTime = UpTime ();
521 static int num_channels = 0;
523 num_channels += OPENAL_GetChannelsPlaying();
524 double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
526 if (0 > deltaTime) // if negative microseconds
527 deltaTime /= -1000000.0;
528 else // else milliseconds
536 float avg_channels = (float)num_channels / (float)frames;
538 ofstream opstream("log.txt",ios::app);
539 opstream << "Average frame count: ";
541 opstream << " frames - ";
542 opstream << avg_channels;
543 opstream << " per frame.\n";
550 if ( stereomode == stereoNone ) {
551 DrawGLScene(stereoCenter);
553 DrawGLScene(stereoLeft);
554 DrawGLScene(stereoRight);
558 // --------------------------------------------------------------------------
566 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
567 #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
570 // cheat here...static destructors are calling glDeleteTexture() after
571 // the context is destroyed and libGL unloaded by SDL_Quit().
572 pglDeleteTextures = glDeleteTextures_doNothing;
573 #endif // __MINGW32__
577 // --------------------------------------------------------------------------
579 static bool IsFocused()
581 return ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_INPUT_FOCUS) != 0);
587 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
588 static char *findBinaryInPath(const char *bin, char *envr)
590 size_t alloc_size = 0;
597 ptr = strchr(start, ':'); /* find next $PATH separator. */
601 size = strlen(start) + strlen(bin) + 2;
602 if (size > alloc_size) {
603 char *x = (char *) realloc(exe, size);
614 /* build full binary path... */
616 if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
620 if (access(exe, X_OK) == 0) { /* Exists as executable? We're done. */
621 strcpy(exe, start); /* i'm lazy. piss off. */
625 start = ptr + 1; /* start points to beginning of next element. */
626 } while (ptr != NULL);
631 return(NULL); /* doesn't exist in path. */
632 } /* findBinaryInPath */
635 char *calcBaseDir(const char *argv0)
637 /* If there isn't a path on argv0, then look through the $PATH for it. */
641 const char *ptr = strrchr((char *)argv0, '/');
642 if (strchr(argv0, '/')) {
643 retval = strdup(argv0);
645 *((char *) strrchr(retval, '/')) = '\0';
649 envr = getenv("PATH");
655 retval = findBinaryInPath(argv0, envr);
660 static inline void chdirToAppPath(const char *argv0)
662 char *dir = calcBaseDir(argv0);
664 #if (defined(__APPLE__) && defined(__MACH__))
665 // Chop off /Contents/MacOS if it's at the end of the string, so we
666 // land in the base of the app bundle.
667 const size_t len = strlen(dir);
668 const char *bundledirs = "/Contents/MacOS";
669 const size_t bundledirslen = strlen(bundledirs);
670 if (len > bundledirslen) {
671 char *ptr = (dir + len) - bundledirslen;
672 if (strcasecmp(ptr, bundledirs) == 0)
683 int main(int argc, char **argv)
688 // !!! FIXME: we could use a Win32 API for this. --ryan.
690 chdirToAppPath(argv[0]);
699 //ofstream os("error.txt");
701 //ofstream os("log.txt");
707 while (!gDone && !tryquit) {
711 // check windows messages
718 while ( SDL_PollEvent( &e ) ) {
719 if (!sdlEventProc(e)) {
730 // allow game chance to pause
735 // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
746 } catch (const std::exception& error) {
749 std::string e = "Caught exception: ";
754 MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
764 // --------------------------------------------------------------------------
767 bool LoadImage(const char * fname, TGAImageRec & tex)
769 if ( tex.data == NULL )
772 return load_image(fname, tex);
775 void ScreenShot(const char * fname)
782 static bool load_image(const char *file_name, TGAImageRec &tex)
784 const char *ptr = strrchr((char *)file_name, '.');
786 if (strcasecmp(ptr + 1, "png") == 0)
787 return load_png(file_name, tex);
788 else if (strcasecmp(ptr + 1, "jpg") == 0)
789 return load_jpg(file_name, tex);
792 STUBBED("Unsupported image type");
797 struct my_error_mgr {
798 struct jpeg_error_mgr pub; /* "public" fields */
799 jmp_buf setjmp_buffer; /* for return to caller */
801 typedef struct my_error_mgr * my_error_ptr;
804 static void my_error_exit(j_common_ptr cinfo)
806 struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
807 longjmp(err->setjmp_buffer, 1);
810 /* stolen from public domain example.c code in libjpg distribution. */
811 static bool load_jpg(const char *file_name, TGAImageRec &tex)
813 struct jpeg_decompress_struct cinfo;
814 struct my_error_mgr jerr;
815 JSAMPROW buffer[1]; /* Output row buffer */
816 int row_stride; /* physical row width in output buffer */
817 FILE *infile = fopen(file_name, "rb");
822 cinfo.err = jpeg_std_error(&jerr.pub);
823 jerr.pub.error_exit = my_error_exit;
824 if (setjmp(jerr.setjmp_buffer)) {
825 jpeg_destroy_decompress(&cinfo);
830 jpeg_create_decompress(&cinfo);
831 jpeg_stdio_src(&cinfo, infile);
832 (void) jpeg_read_header(&cinfo, TRUE);
834 cinfo.out_color_space = JCS_RGB;
835 cinfo.quantize_colors = 0;
836 (void) jpeg_calc_output_dimensions(&cinfo);
837 (void) jpeg_start_decompress(&cinfo);
839 row_stride = cinfo.output_width * cinfo.output_components;
840 tex.sizeX = cinfo.output_width;
841 tex.sizeY = cinfo.output_height;
844 while (cinfo.output_scanline < cinfo.output_height) {
845 buffer[0] = (JSAMPROW)(char *)tex.data +
846 ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride;
847 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
850 (void) jpeg_finish_decompress(&cinfo);
851 jpeg_destroy_decompress(&cinfo);
858 /* stolen from public domain example.c code in libpng distribution. */
859 static bool load_png(const char *file_name, TGAImageRec &tex)
861 bool hasalpha = false;
862 png_structp png_ptr = NULL;
863 png_infop info_ptr = NULL;
864 png_uint_32 width, height;
865 int bit_depth, color_type, interlace_type;
866 png_byte **rows = NULL;
868 png_byte **row_pointers = NULL;
869 FILE *fp = fopen(file_name, "rb");
872 cerr << file_name << " not found" << endl;
876 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
880 info_ptr = png_create_info_struct(png_ptr);
881 if (info_ptr == NULL)
884 if (setjmp(png_jmpbuf(png_ptr)))
887 png_init_io(png_ptr, fp);
888 png_read_png(png_ptr, info_ptr,
889 PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
891 png_get_IHDR(png_ptr, info_ptr, &width, &height,
892 &bit_depth, &color_type, &interlace_type, NULL, NULL);
894 if (bit_depth != 8) // transform SHOULD handle this...
897 if (color_type & PNG_COLOR_MASK_PALETTE) // !!! FIXME?
900 if ((color_type & PNG_COLOR_MASK_COLOR) == 0) // !!! FIXME?
903 hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
904 row_pointers = png_get_rows(png_ptr, info_ptr);
909 png_byte *dst = tex.data;
910 for (int i = height - 1; i >= 0; i--) {
911 png_byte *src = row_pointers[i];
912 for (int j = 0; j < width; j++) {
924 png_byte *dst = tex.data;
925 int pitch = width * 4;
926 for (int i = height - 1; i >= 0; i--, dst += pitch)
927 memcpy(dst, row_pointers[i], pitch);
937 cerr << "There was a problem loading " << file_name << endl;
939 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
946 bool save_image(const char *file_name)
948 const char *ptr = strrchr((char *)file_name, '.');
950 if (strcasecmp(ptr + 1, "png") == 0)
951 return save_png(file_name);
954 STUBBED("Unsupported image type");
959 static bool save_png(const char *file_name)
962 png_structp png_ptr = NULL;
963 png_infop info_ptr = NULL;
966 fp = fopen(file_name, "wb");
970 png_bytep *row_pointers = new png_bytep[kContextHeight];
971 png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
972 if ((!screenshot) || (!row_pointers))
976 glReadPixels(0, 0, kContextWidth, kContextHeight,
977 GL_RGB, GL_UNSIGNED_BYTE, screenshot);
978 if (glGetError() != GL_NO_ERROR)
981 for (int i = 0; i < kContextHeight; i++)
982 row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3);
984 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
988 info_ptr = png_create_info_struct(png_ptr);
989 if (info_ptr == NULL)
992 if (setjmp(png_jmpbuf(png_ptr)))
995 png_init_io(png_ptr, fp);
997 if (setjmp(png_jmpbuf(png_ptr)))
1000 png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
1001 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1002 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1004 png_write_info(png_ptr, info_ptr);
1006 if (setjmp(png_jmpbuf(png_ptr)))
1009 png_write_image(png_ptr, row_pointers);
1011 if (setjmp(png_jmpbuf(png_ptr)))
1014 png_write_end(png_ptr, NULL);
1018 png_destroy_write_struct(&png_ptr, &info_ptr);
1019 delete[] screenshot;
1020 delete[] row_pointers;
1024 unlink(ConvertFileName(file_name));