Beispiel #1
0
int main( int argc, char **argv )
{    
	MemStartCheck();
	{ char* test = new char[16]; delete [] test; }

	SDL_Surface *surface = 0;

	// SDL initialization steps.
    if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK ) < 0 )
	{
	    fprintf( stderr, "SDL initialization failed: %s\n", SDL_GetError( ) );
		exit( 1 );
	}
	SDL_EnableKeyRepeat( 0, 0 );
	SDL_EnableUNICODE( 1 );

	const SDL_version* sversion = SDL_Linked_Version();
	GLOUTPUT(( "SDL: major %d minor %d patch %d\n", sversion->major, sversion->minor, sversion->patch ));

	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
	SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8);

	if ( multisample ) {
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, multisample );
	}

	int	videoFlags  = SDL_OPENGL;          /* Enable OpenGL in SDL */
		videoFlags |= SDL_GL_DOUBLEBUFFER; /* Enable double buffering */

	if ( fullscreen )
		videoFlags |= SDL_FULLSCREEN;
	else
		videoFlags |= SDL_RESIZABLE;

#ifdef TEST_ROTATION
	screenWidth  = SCREEN_WIDTH;
	screenHeight = SCREEN_HEIGHT;
#else
	screenWidth  = SCREEN_HEIGHT;
	screenHeight = SCREEN_WIDTH;
#endif

	if ( argc == 3 ) {
		screenWidth = atoi( argv[1] );
		screenHeight = atoi( argv[2] );
		if ( screenWidth <= 0 ) screenWidth = IPOD_SCREEN_WIDTH;
		if ( screenHeight <= 0 ) screenHeight = IPOD_SCREEN_HEIGHT;
	}

	// Note that our output surface is rotated from the iPod.
	//surface = SDL_SetVideoMode( IPOD_SCREEN_HEIGHT, IPOD_SCREEN_WIDTH, 32, videoFlags );
	surface = SDL_SetVideoMode( screenWidth, screenHeight, 32, videoFlags );
	GLASSERT( surface );

	int stencil = 0;
	int depth = 0;
	SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &stencil );
	glGetIntegerv( GL_DEPTH_BITS, &depth );
	GLOUTPUT(( "SDL surface created. w=%d h=%d bpp=%d stencil=%d depthBits=%d\n", 
				surface->w, surface->h, surface->format->BitsPerPixel, stencil, depth ));

    /* Verify there is a surface */
    if ( !surface ) {
	    fprintf( stderr,  "Video mode set failed: %s\n", SDL_GetError( ) );
	    exit( 1 );
	}

    SDL_JoystickEventState(SDL_ENABLE);
    SDL_Joystick* joystick = SDL_JoystickOpen(0);
	if ( joystick ) {
		GLOUTPUT(( "Joystick '%s' open.\n", SDL_JoystickName(0) ));
	}

	int r = glewInit();
	GLASSERT( r == GL_NO_ERROR );

	// Calling this seems to confuse my ATI driver and cause lag / event back up?
//#ifdef TEST_FULLSPEED	
//	wglSwapIntervalEXT( 0 );	// vsync
//#else
//	wglSwapIntervalEXT( 1 );	// vsync
//#endif

	const unsigned char* vendor   = glGetString( GL_VENDOR );
	const unsigned char* renderer = glGetString( GL_RENDERER );
	const unsigned char* version  = glGetString( GL_VERSION );

	GLOUTPUT(( "OpenGL vendor: '%s'  Renderer: '%s'  Version: '%s'\n", vendor, renderer, version ));

	Audio_Init();

	bool done = false;
	bool zooming = false;
    SDL_Event event;

	float yRotation = 45.0f;
	grinliz::Vector2I mouseDown = { 0, 0 };
	grinliz::Vector2I prevMouseDown = { 0, 0 };
	U32 prevMouseDownTime = 0;

	int zoomX = 0;
	int zoomY = 0;

	void* game = 0;
	bool mapMakerMode = false;

	WIN32_FIND_DATA findFileData;
	HANDLE h;
	h = FindFirstFile( ".\\mods\\*.xwdb", &findFileData );
	if ( h != INVALID_HANDLE_VALUE ) {
		BOOL findResult = TRUE;
		while( findResult && nModDB < GAME_MAX_MOD_DATABASES ) {
			grinliz::GLString* str = new grinliz::GLString( ".\\mods\\" );
			str->append( findFileData.cFileName );
			databases[nModDB++] = str;
			GameAddDatabase( str->c_str() );
			findResult = FindNextFile( h, &findFileData );
		}
		FindClose( h );
	}

	if ( argc > 3 ) {
		// -- MapMaker -- //
		Engine::mapMakerMode = true;

		TileSetDesc desc;
		desc.set = "FARM";
		desc.size = 16;
		desc.type = "TILE";
		desc.variation = 0;

		if ( argc > 2 ) {
			desc.set = argv[2];
			GLASSERT( strlen( desc.set ) == 4 );
		}

		if ( argc > 3 ) {
			desc.size = atol( argv[3] );
			GLASSERT( desc.size == 16 || desc.size == 32 || desc.size == 48 || desc.size == 64 );
		}

		if ( argc > 4 ) {
			desc.type = argv[4];
			GLASSERT( strlen( desc.type ) == 4 );
		}

		if ( argc > 5 ) {
			desc.variation = atol( argv[5] );
			GLASSERT( desc.variation >= 0 && desc.variation < 100 );
		}

		game = new Game( screenWidth, screenHeight, rotation, ".\\resin\\", desc );
		mapMakerMode = true;
	}
	else {
		game = NewGame( screenWidth, screenHeight, rotation, ".\\", tvMode );
	}


#if SEND_CRASH_LOGS
	// Can't call this until after the game is created!
	if ( !SettingsManager::Instance()->GetSuppressCrashLog() ) {
		// Check for a "didn't crash" file.
		FILE* fp = fopen( "UFO_Running.txt", "r" );
		if ( fp ) {
			fseek( fp, 0, SEEK_END );
			long len = ftell( fp );
			if ( len > 1 ) {
				// Wasn't deleted.
				PostCurrentGame();
			}
			fclose( fp );
		}
	}
	{
		FILE* fp = fopen( "UFO_Running.txt", "w" );
		if ( fp ) {
			fprintf( fp, "Game running." );
			fclose( fp );
		}
	}
#endif


#ifndef TEST_FULLSPEED
	SDL_TimerID timerID = SDL_AddTimer( TIME_BETWEEN_FRAMES, TimerCallback, 0 );
#endif

	bool L2Down = false;
	bool R2Down = false;
	grinliz::Vector2F joystickAxis[2] = { 0, 0 };

	// ---- Main Loop --- //
#ifdef TEST_FULLSPEED	
	while ( !done ) {
		if ( SDL_PollEvent( &event ) )
#else
	while ( !done && SDL_WaitEvent( &event ) )
#endif
	{
		// The user event shouldn't be duplicated...if there are 2, pull out the dupe.
		if ( event.type == SDL_USEREVENT ) {
			SDL_Event e;
			while( true ) {
				int n = SDL_PeepEvents( &e, 1, SDL_PEEKEVENT, SDL_ALLEVENTS );		
				if ( n == 1 && e.type == SDL_USEREVENT ) {
					SDL_PeepEvents( &e, 1, SDL_GETEVENT, SDL_ALLEVENTS );
				}
				else {
					break;
				}
			}
		}

		switch( event.type )
		{
			case SDL_VIDEORESIZE:
				screenWidth = event.resize.w;
				screenHeight = event.resize.h;
				surface = SDL_SetVideoMode( screenWidth, screenHeight, 32, videoFlags );
				GameDeviceLoss( game );
				GameResize( game, event.resize.w, event.resize.h, rotation );
				break;

			/*
				A: 0		Triggers: axis=2
				X: 2
				Y: 3
				B: 1
				L1: 4
				R1: 5
			*/

			case SDL_JOYBUTTONDOWN:
			case SDL_JOYBUTTONUP:
				//GLOUTPUT(( "Button %d.\n", event.jbutton.button ));
				switch( event.jbutton.button ) {
				case 0:	GameJoyButton( game, GAME_JOY_BUTTON_DOWN,	event.type == SDL_JOYBUTTONDOWN );	break;
				case 1:	GameJoyButton( game, GAME_JOY_BUTTON_RIGHT,	event.type == SDL_JOYBUTTONDOWN );	break;
				case 2:	GameJoyButton( game, GAME_JOY_BUTTON_LEFT,	event.type == SDL_JOYBUTTONDOWN );	break;
				case 3:	GameJoyButton( game, GAME_JOY_BUTTON_UP,	event.type == SDL_JOYBUTTONDOWN );	break;
				case 4: GameJoyButton( game, GAME_JOY_L1,			event.type == SDL_JOYBUTTONDOWN );	break;
				case 5: GameJoyButton( game, GAME_JOY_R1,			event.type == SDL_JOYBUTTONDOWN );	break;
				}
				break;

			case SDL_JOYAXISMOTION:
				//GLOUTPUT(( "Axis %d to %d.\n", event.jaxis.axis, event.jaxis.value ));

				// axis2, posL, negR
				if ( event.jaxis.axis == 2 ) {
					int value = event.jaxis.value;
					static const int T = 10*1000;
					if ( value > 10 ) {
						if ( !L2Down && value > T ) {
							L2Down = true;
							GameJoyButton( game, GAME_JOY_L2, true );
						}
						else if ( L2Down && value < T ) {
							L2Down = false;
							GameJoyButton( game, GAME_JOY_L2, false );
						}
					}
					else if ( value < -10 ) {
						if ( !R2Down && value < -T ) {
							R2Down = true;
							GameJoyButton( game, GAME_JOY_R2, true );
						}
						else if ( R2Down && value > -T ) {
							R2Down = false;
							GameJoyButton( game, GAME_JOY_R2, false );
						}
					}
				}
				else {
					int value = event.jaxis.value;
					double normal = (double)value/32768.0f;
					int axis = -1;
					int stick = -1;

					switch( event.jaxis.axis ) {
						case 0:	axis=0;	stick=0;					break;
						case 1: axis=1; stick=0; normal *= -1.0;	break;
						case 3: axis=1;	stick=1; normal *= -1.0f;	break;
						case 4: axis=0; stick=1;					break;
						default: break;
					}

					if ( axis >= 0 && stick >= 0 ) {
						joystickAxis[stick].X(axis) = (float)normal;
					}
				}


				break;

			case SDL_JOYHATMOTION:
				GameJoyDPad( game, event.jhat.value );
				break;

			case SDL_KEYDOWN:
			{
				SDLMod sdlMod = SDL_GetModState();

				if ( mapMakerMode && event.key.keysym.sym >= SDLK_0 && event.key.keysym.sym <= SDLK_9 ) {
					int index = 0;
					switch ( event.key.keysym.sym ) {
					case SDLK_1:	index = 0;	break;
					case SDLK_2:	index = 1;	break;
					case SDLK_3:	index = 2;	break;
					case SDLK_4:	index = 3;	break;
					case SDLK_5:	index = 4;	break;
					case SDLK_6:	index = 5;	break;
					case SDLK_7:	index = 6;	break;
					case SDLK_8:	index = 7;	break;
					case SDLK_9:	index = 8;	break;
					case SDLK_0:	index = 9;	break;
					};

					const U8* light = ((Game*)game)->engine->GetMap()->DayTime() ? dayLight : nightLight;
					static const float INV = 1.0f/255.0f;

					U8 r = light[index*3+0];
					U8 g = light[index*3+1];
					U8 b = light[index*3+2];

					if ( sdlMod & sdlMod & ( KMOD_LSHIFT | KMOD_RSHIFT ) ) {
						if ( index < 6 ) {
							// Average with shade.
							r = (light[index*3+0] + light[SHADE*3+0]) / 2;
							g = (light[index*3+1] + light[SHADE*3+1]) / 2;
							b = (light[index*3+2] + light[SHADE*3+2]) / 2;
						}
						else if ( index > 6 ) {
							// make darker (index 6 is the darkest. SHIFT does nothing.)
							int m = index-1;
							r = (light[index*3+0] + light[m*3+0]) / 2;
							g = (light[index*3+1] + light[m*3+1]) / 2;
							b = (light[index*3+2] + light[m*3+2]) / 2;
						}
					}
					((Game*)game)->SetLightMap( (float)r * INV, (float)g * INV, (float)b * INV );
				}

				switch ( event.key.keysym.sym )
				{
					case SDLK_ESCAPE:
						{
							//int handled = GameHotKey( game, GAME_HK_BACK );
#ifdef DEBUG
							// only escape out in debug mode
							// if ( !handled ) 
							done = true;
#endif
						}
						break;

					case SDLK_F4:
						if ( sdlMod & ( KMOD_RALT | KMOD_LALT ) )
							done = true;
						break;

#ifdef SIM_GAMEPAD
					case SDLK_RIGHT:	GameJoyDPad( game, GAME_JOY_DPAD_RIGHT );	break;
					case SDLK_LEFT:		GameJoyDPad( game, GAME_JOY_DPAD_LEFT );	break;
					case SDLK_UP:		GameJoyDPad( game, GAME_JOY_DPAD_UP );		break;
					case SDLK_DOWN:		GameJoyDPad( game, GAME_JOY_DPAD_DOWN );	break;
					case SDLK_1:		GameJoyButton( game, 1, true );				break;
					case SDLK_2:		GameJoyButton( game, 2, true );				break;
					case SDLK_3:		GameJoyButton( game, 3, true );				break;
					case SDLK_4:		GameJoyButton( game, 4, true );				break;

#else
					case SDLK_RIGHT:
						if ( !mapMakerMode ) {
							if ( sdlMod & (KMOD_RCTRL|KMOD_LCTRL) )
								GameHotKey( game, GAME_HK_ROTATE_CW );
							else
								GameHotKey( game, GAME_HK_NEXT_UNIT );
						}
						break;

					case SDLK_LEFT:
						if ( !mapMakerMode ) {
							if ( sdlMod & (KMOD_RCTRL|KMOD_LCTRL) )
								GameHotKey( game, GAME_HK_ROTATE_CCW );
							else
								GameHotKey( game, GAME_HK_PREV_UNIT );
						}
						break;
#endif
					case SDLK_u:
						if ( mapMakerMode ) {
							((Game*)game)->engine->camera.SetTilt( -90.0f );
							((Game*)game)->engine->camera.SetPosWC( 8.f, 90.f, 8.f );
							((Game*)game)->engine->camera.SetYRotation( 0.0f );
						}
						else {
							GameHotKey( game, GAME_HK_TOGGLE_ROTATION_UI | GAME_HK_TOGGLE_NEXT_UI );
						}
						break;

					case SDLK_o:
						if ( mapMakerMode ) {
							cameraIso = !cameraIso;
							((Game*)game)->engine->CameraIso( cameraIso, true, (float)((Game*)game)->engine->GetMap()->Width(), (float)((Game*)game)->engine->GetMap()->Height() );
						}
						break;

					case SDLK_s:
						if ( mapMakerMode ) {
							((Game*)game)->SuppressText( true );
						}
						GameDoTick( game, SDL_GetTicks() );
						SDL_GL_SwapBuffers();
						if ( mapMakerMode ) {
							((Game*)game)->SuppressText( false );
						}
						ScreenCapture( "cap" );
						break;

					case SDLK_l:
						if ( mapMakerMode ) {
							const Surface* lightmap = ((Game*)game)->engine->GetMap()->GetLightMap();
							SaveLightMap( lightmap );
						}
						break;

					case SDLK_d:
						GameHotKey( game, GAME_HK_TOGGLE_DEBUG_TEXT );
						break;

					case SDLK_DELETE:
						if ( mapMakerMode )
							((Game*)game)->DeleteAtSelection(); 
						break;

					case SDLK_KP9:			
						if ( mapMakerMode )
							((Game*)game)->RotateSelection( -1 );			
						break;

					case SDLK_r:
					case SDLK_KP7:			
						if ( mapMakerMode )
							((Game*)game)->RotateSelection( 1 );			
						break;

					case SDLK_KP8:			
						if ( mapMakerMode )
							((Game*)game)->DeltaCurrentMapItem(16);			
						break;

					case SDLK_KP5:			
						if ( mapMakerMode )
							((Game*)game)->DeltaCurrentMapItem(-16);		
						break;

					case SDLK_KP6:			
						if ( mapMakerMode )
							((Game*)game)->DeltaCurrentMapItem(1); 			
						break;

					case SDLK_KP4:			
						if ( mapMakerMode )
							((Game*)game)->DeltaCurrentMapItem(-1);			
						break;

					case SDLK_p:
						//if ( mapMakerMode )
						{
							int pathing = (((Game*)game)->ShowingPathing() + 1) % 3;
							((Game*)game)->ShowPathing( pathing );
						}
						break;

					case SDLK_t:
						if ( mapMakerMode )
							((Game*)game)->engine->GetMap()->SetDayTime( !((Game*)game)->engine->GetMap()->DayTime() );
						break;

					case SDLK_v:
						((Game*)game)->ToggleTV();
						break;

					case SDLK_m:
						if ( mapMakerMode )
							((Game*)game)->engine->EnableMetadata( !((Game*)game)->engine->IsMetadataEnabled() );
						break;

					default:
						break;
				}
/*					GLOUTPUT(( "fov=%.1f rot=%.1f h=%.1f\n", 
							game->engine.fov, 
							game->engine.camera.Tilt(), 
							game->engine.camera.PosWC().y ));
*/
			}
			break;

#ifdef SIM_GAMEPAD
			case SDL_KEYUP:
			{
				switch ( event.key.keysym.sym )
				{
					case SDLK_1:		GameJoyButton( game, 1, false );				break;
					case SDLK_2:		GameJoyButton( game, 2, false );				break;
					case SDLK_3:		GameJoyButton( game, 3, false );				break;
					case SDLK_4:		GameJoyButton( game, 4, false );				break;
				}
			}
			break;
#endif

			case SDL_MOUSEBUTTONDOWN:
			{
				int x, y;
				TransformXY( event.button.x, event.button.y, &x, &y );

				mouseDown.Set( event.button.x, event.button.y );

				if ( event.button.button == 1 ) {
					GameTap( game, GAME_TAP_DOWN, x, y );
				}
				else if ( event.button.button == 3 ) {
					GameTap( game, GAME_TAP_CANCEL, x, y );
					zooming = true;
					//GameCameraRotate( game, GAME_ROTATE_START, 0.0f );
					SDL_GetRelativeMouseState( &zoomX, &zoomY );
				}
			}
			break;

			case SDL_MOUSEBUTTONUP:
			{
				int x, y;
				TransformXY( event.button.x, event.button.y, &x, &y );

				if ( event.button.button == 3 ) {
					zooming = false;
				}
				if ( event.button.button == 1 ) {
					GameTap( game, GAME_TAP_UP, x, y );
				}
			}
			break;

			case SDL_MOUSEMOTION:
			{
				SDL_GetRelativeMouseState( &zoomX, &zoomY );
				int state = SDL_GetMouseState(NULL, NULL);
				int x, y;
				TransformXY( event.button.x, event.button.y, &x, &y );

				if ( state & SDL_BUTTON(1) ) {
					GameTap( game, GAME_TAP_MOVE, x, y );
				}
				else if ( zooming && (state & SDL_BUTTON(3)) ) {
					float deltaZoom = 0.01f * (float)zoomY;
					GameZoom( game, GAME_ZOOM_DISTANCE, deltaZoom );
					GameCameraRotate( game, (float)(zoomX)*0.5f );
				}
				else if ( ( ( state & SDL_BUTTON(1) ) == 0 ) ) {
					((Game*)game)->MouseMove( x, y );
				}
			}
			break;

			case SDL_QUIT:
			{
				done = true;
			}
			break;

			case SDL_USEREVENT:
			{
				glEnable( GL_DEPTH_TEST );
				glDepthFunc( GL_LEQUAL );

				for( int stick=0; stick<2; ++stick ) {
					if ( joystickAxis[stick].x || joystickAxis[stick].y ) {
						GameJoyStick( game, stick, joystickAxis[stick].x, joystickAxis[stick].y );
					}
				}
				GameDoTick( game, SDL_GetTicks() );
				SDL_GL_SwapBuffers();

				int databaseID=0, size=0, offset=0;
				// FIXME: account for databaseID when looking up sound.
				while ( GamePopSound( game, &databaseID, &offset, &size ) ) {
					Audio_PlayWav( "./res/uforesource.db", offset, size );
				}
			};

			default:
				break;
		}
#ifdef TEST_FULLSPEED	
		}

		glEnable( GL_DEPTH_TEST );
		glDepthFunc( GL_LEQUAL );

		GameDoTick( game, SDL_GetTicks() );
		SDL_GL_SwapBuffers();
	}
#else
	}
Beispiel #2
0
int main(int argc, char **argv)
{
	MemStartCheck();
	{ char* test = new char[16]; delete[] test; }
	grinliz::TestContainers();

	{
		grinliz::GLString releasePath;
		GetSystemPath(GAME_SAVE_DIR, "release_log.txt", &releasePath);
		SetReleaseLog(fopen(releasePath.c_str(), "w"));
	}
	GLOUTPUT_REL(("Altera startup. version'%s'\n", VERSION));

	SDL_version compiled;
	SDL_version linked;

	SDL_VERSION(&compiled);
	SDL_GetVersion(&linked);

	GLOUTPUT_REL(("SDL version compiled: %d.%d.%d\n", compiled.major, compiled.minor, compiled.patch));
	GLOUTPUT_REL(("SDL version linked:   %d.%d.%d\n", linked.major, linked.minor, linked.patch));
	GLASSERT((linked.major == compiled.major && linked.minor == compiled.minor));

	// SDL initialization steps.
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_EVENTS) < 0)
	{
		fprintf(stderr, "SDL initialization failed: %s\n", SDL_GetError());
		exit(1);
	}

	//  OpenGL 4.3 provides full compatibility with OpenGL ES 3.0.
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

#ifdef DEBUG
#if 0	// I was hoping to get to Angle on intel - may still be able to. But
	// as is this gets HW mode, which crashes in a function that should
	// be supported. The Intel drivers are so terrible. As of this writing,
	// you can't specify the DX version of ES:
	//		http://forums.libsdl.org/viewtopic.php?t=9770&highlight=angle+opengl
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);	// driver supports 2 and 3. Both crash.
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#endif
#if 0
	// 3.0 context.
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#endif
#if 0
	// The trickier 3.2 context.
	// No GL_QUADs anymore.
	// All the attributes need to be floats.
	// No ALPHA textures.
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#endif
#if 0
	// In theory the minimum supported version:
	// has instancing, and modern shader syntax
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
#endif
#if 0
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#endif
#endif

	if (multisample) {
		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisample);
	}

	SDL_DisplayMode displayMode;
	SDL_GetCurrentDisplayMode(0, &displayMode);

	int screenX = displayMode.w / 8;
	int screenY = displayMode.h / 8;
	int screenWidth = displayMode.w * 3 / 4;
	int screenHeight = displayMode.h * 3 / 4;

	if (argc == 3) {
		screenWidth = atoi(argv[1]);
		screenHeight = atoi(argv[2]);
		if (screenWidth <= 0) screenWidth = SCREEN_WIDTH;
		if (screenHeight <= 0) screenHeight = SCREEN_HEIGHT;
	}

	restoreWidth = screenWidth;
	restoreHeight = screenHeight;

	SDL_Window *screen = SDL_CreateWindow("Altera",
		screenX, screenY, screenWidth, screenHeight,
		/*SDL_WINDOW_FULLSCREEN | */ SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	GLASSERT(screen);
	SDL_GL_CreateContext(screen);

	int stencil = 0;
	int depth = 0;
	CHECK_GL_ERROR;
	SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil);
	SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
	glGetError();	// the above stencil/depth query sometimes does throw an error.
	glGetError();	// 2 queries, 2 errors.
	CHECK_GL_ERROR;
	GLOUTPUT_REL(("SDL screen created. stencil=%d depthBits=%d\n", stencil, depth));

	/* Verify there is a surface */
	if (!screen) {
		fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError());
		exit(1);
	}

	CHECK_GL_ERROR;
	glewExperimental = GL_TRUE;
	int r = glewInit();
	GLASSERT(r == GL_NO_ERROR);
	(void)r;

	while (glGetError() != GL_NO_ERROR) {
		// around again
	}
	CHECK_GL_ERROR;

	const unsigned char* vendor = glGetString(GL_VENDOR);
	const unsigned char* renderer = glGetString(GL_RENDERER);
	const unsigned char* version = glGetString(GL_VERSION);

	GLOUTPUT_REL(("OpenGL vendor: '%s'  Renderer: '%s'  Version: '%s'\n", vendor, renderer, version));
	CHECK_GL_ERROR;

	bool done = false;
	bool zooming = false;
	SDL_Event event;

	grinliz::Vector2I mouseDown = { 0, 0 };
	grinliz::Vector2I rightMouseDown = { 0, 0 };

	int zoomX = 0;
	int zoomY = 0;
	// Used to compute fingers close, but problems:
	// - really to the OS to do that, because of all the tuning
	// - the coordinates are in windows normalized, so can't get the physical distance reliably.
	//bool fingersClose = true;
	int nFingers = 0;

	void* game = NewGame(screenWidth, screenHeight, 0);

	int modKeys = SDL_GetModState();
	U32 tickTimer = 0, lastTick = 0, thisTick = 0;

#ifdef OUTPUT_MOUSE_AND_TOUCH
	int value = GetSystemMetrics(SM_DIGITIZER);
	if (value & NID_INTEGRATED_TOUCH) GLOUTPUT(("NID_INTEGRATED_TOUCH\n"));
	if (value & NID_MULTI_INPUT) GLOUTPUT(("NID_MULTI_INPUT\n"));
	if (value & NID_READY) GLOUTPUT(("NID_READY\n"));
#endif

	grinliz::Vector2F multiTouchStart = { 0, 0 };

	// ---- Main Loop --- //
	while (!done) {
		while (SDL_PollEvent(&event)) {

			switch (event.type)
			{
				case SDL_WINDOWEVENT:
				if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
					screenWidth = event.window.data1;
					screenHeight = event.window.data2;
					GameDeviceLoss(game);
					GameResize(game, screenWidth, screenHeight, 0);
				}
				break;

				case SDL_KEYUP:
				switch (event.key.keysym.scancode)
				{
					case SDL_SCANCODE_LCTRL:	modKeys = modKeys & (~KMOD_LCTRL);		break;
					case SDL_SCANCODE_RCTRL:	modKeys = modKeys & (~KMOD_RCTRL);		break;
					case SDL_SCANCODE_LSHIFT:	modKeys = modKeys & (~KMOD_LSHIFT);		break;
					case SDL_SCANCODE_RSHIFT:	modKeys = modKeys & (~KMOD_RSHIFT);		break;
					default:
					break;
				}
				break;

				case SDL_KEYDOWN:
				{
					// sym or scancode? I used a dvorak keyboard, so appreciate
					// every day the difference. However, AWSD support is the 
					// primary thing so scancode is hopefully the better choice.
					switch (event.key.keysym.scancode)
					{
						case SDL_SCANCODE_LCTRL:	modKeys = modKeys | KMOD_LCTRL;		break;
						case SDL_SCANCODE_RCTRL:	modKeys = modKeys | KMOD_RCTRL;		break;
						case SDL_SCANCODE_LSHIFT:	modKeys = modKeys | KMOD_LSHIFT;	break;
						case SDL_SCANCODE_RSHIFT:	modKeys = modKeys | KMOD_RSHIFT;	break;

						case SDL_SCANCODE_F4:
						{
							int sdlMod = SDL_GetModState();
							if (sdlMod & (KMOD_RALT | KMOD_LALT))
								done = true;
						}
						break;

						case SDL_SCANCODE_ESCAPE:	GameHotKey(game, GAME_HK_ESCAPE);			break;
						case SDL_SCANCODE_SPACE:	GameHotKey(game, GAME_HK_TOGGLE_PAUSE);		break;
						case SDL_SCANCODE_RETURN:	GameHotKey(game, GAME_HK_DEBUG_ACTION);		break;
						case SDL_SCANCODE_F1:	GameHotKey(game, GAME_HK_TOGGLE_DEBUG_TEXT);	break;
						case SDL_SCANCODE_F2:	GameHotKey(game, GAME_HK_TOGGLE_DEBUG_UI);		break;
							// F3: screenshot
						case SDL_SCANCODE_TAB:	GameHotKey(game, GAME_HK_CAMERA_TOGGLE);		break;
						case SDL_SCANCODE_HOME:	GameHotKey(game, GAME_HK_CAMERA_CORE);			break;
						case SDL_SCANCODE_END:	GameHotKey(game, GAME_HK_CAMERA_AVATAR);		break;
						case SDL_SCANCODE_PAGEUP:	GameHotKey(game, GAME_HK_TELEPORT_AVATAR);	break;

							//case SDLK_a: reserved
						case SDL_SCANCODE_B:	GameHotKey(game, GAME_HK_CHEAT_GOLD);			break;
						case SDL_SCANCODE_C:	GameHotKey(game, GAME_HK_ATTACH_CORE);			break;
							//case SDLK_d: reserved
						case SDL_SCANCODE_E:	GameHotKey(game, GAME_HK_CHEAT_ELIXIR);			break;
						case SDL_SCANCODE_H:	GameHotKey(game, GAME_HK_TOGGLE_PATHING);		break;
						case SDL_SCANCODE_I:	GameHotKey(game, GAME_HK_TOGGLE_AI_DEBUG);		break;
						case SDL_SCANCODE_K:	GameHotKey(game, GAME_HK_CHEAT_CRYSTAL);		break;
						case SDL_SCANCODE_M:	GameHotKey(game, GAME_HK_MAP);					break;
						case SDL_SCANCODE_P:	GameHotKey(game, GAME_HK_TOGGLE_PERF);			break;
						case SDL_SCANCODE_Q:	GameHotKey(game, GAME_HK_CHEAT_HERD);			break;
							//case SDLK_s: reserved
						case SDL_SCANCODE_T:	GameHotKey(game, GAME_HK_CHEAT_TECH);			break;
						case SDL_SCANCODE_U:	GameHotKey(game, GAME_HK_TOGGLE_UI);			break;
							//case SDLK_w: reserved

						case SDL_SCANCODE_1:	GameHotKey(game, GAME_HK_TOGGLE_GLOW);			break;
						case SDL_SCANCODE_2:	GameHotKey(game, GAME_HK_TOGGLE_PARTICLE);		break;
						case SDL_SCANCODE_3:	GameHotKey(game, GAME_HK_TOGGLE_VOXEL);			break;
						case SDL_SCANCODE_4:	GameHotKey(game, GAME_HK_TOGGLE_SHADOW);		break;
						case SDL_SCANCODE_5:	GameHotKey(game, GAME_HK_TOGGLE_BOLT);			break;

						case SDL_SCANCODE_F3:
						GameDoTick(game, SDL_GetTicks());
						SDL_GL_SwapWindow(screen);
						ScreenCapture();
						break;

						case SDL_SCANCODE_F11:
						{
							if (fullscreen) {
								// SDL_RestoreWindow doesn't seem to work as I expect.
								SDL_SetWindowFullscreen(screen, 0);
								SDL_SetWindowSize(screen, restoreWidth, restoreHeight);
								fullscreen = false;
							}
							else {
								restoreWidth = screenWidth;
								restoreHeight = screenHeight;
								SDL_SetWindowFullscreen(screen, SDL_WINDOW_FULLSCREEN_DESKTOP);
								fullscreen = true;
							}
						}
						break;

						default:
						break;
					}
				}
				break;

				case SDL_MOUSEBUTTONDOWN:
				{
					int x = event.button.x;
					int y = event.button.y;

					int mod = 0;
					if (modKeys & (KMOD_LSHIFT | KMOD_RSHIFT))    mod = GAME_TAP_MOD_SHIFT;
					else if (modKeys & (KMOD_LCTRL | KMOD_RCTRL)) mod = GAME_TAP_MOD_CTRL;

					if (nFingers > 1) {
						// do nothing
					}
					else if (event.button.button == SDL_BUTTON_LEFT) {
						#ifdef OUTPUT_MOUSE_AND_TOUCH
						GLOUTPUT(("Left mouse down %d %d\n", x, y));
						#endif
						mouseDown.Set(event.button.x, event.button.y);
						GameTap(game, GAME_TAP_DOWN, x, y, mod);
					}
					else if (event.button.button == SDL_BUTTON_RIGHT) {
						#ifdef OUTPUT_MOUSE_AND_TOUCH
						GLOUTPUT(("Right mouse down %d %d\n", x, y));
						#endif
						GameTap(game, GAME_TAP_CANCEL, x, y, mod);
						rightMouseDown.Zero();
						if (mod == 0) {
							rightMouseDown.Set(event.button.x, event.button.y);
							GameCameraPan(game, GAME_PAN_START, float(x), float(y));
						}
						else if (mod == GAME_TAP_MOD_CTRL) {
							zooming = true;
							//GameCameraRotate( game, GAME_ROTATE_START, 0.0f );
							SDL_GetRelativeMouseState(&zoomX, &zoomY);
						}
					}
				}
				break;

				case SDL_MOUSEBUTTONUP:
				{
					int x = event.button.x;
					int y = event.button.y;

					if (event.button.button == SDL_BUTTON_RIGHT) {
						#ifdef OUTPUT_MOUSE_AND_TOUCH
						GLOUTPUT(("Right mouse up %d %d\n", x, y));
						#endif
						zooming = false;
						if (!rightMouseDown.IsZero()) {
							GameCameraPan(game, GAME_PAN_END, float(x), float(y));
							rightMouseDown.Zero();
						}
					}
					if (event.button.button == SDL_BUTTON_LEFT) {
						if (!mouseDown.IsZero()) {	// filter out mouse events that become finger events.
							#ifdef OUTPUT_MOUSE_AND_TOUCH
							GLOUTPUT(("Left mouse up %d %d\n", x, y));
							#endif
							int mod = 0;
							if (modKeys & (KMOD_LSHIFT | KMOD_RSHIFT))    mod = GAME_TAP_MOD_SHIFT;
							else if (modKeys & (KMOD_LCTRL | KMOD_RCTRL)) mod = GAME_TAP_MOD_CTRL;
							GameTap(game, GAME_TAP_UP, x, y, mod);
						}
					}
				}
				break;

				case SDL_MOUSEMOTION:
				{
					SDL_GetRelativeMouseState(&zoomX, &zoomY);
					int state = event.motion.state;
					int x = event.motion.x;
					int y = event.motion.y;

					int mod = 0;
					if (modKeys & (KMOD_LSHIFT | KMOD_RSHIFT))    mod = GAME_TAP_MOD_SHIFT;
					else if (modKeys & (KMOD_LCTRL | KMOD_RCTRL)) mod = GAME_TAP_MOD_CTRL;

					if (nFingers > 1) {
						// Do nothing.
						// Multi touch in progress.
					}
					else if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
						if (!mouseDown.IsZero()) {
							#ifdef OUTPUT_MOUSE_AND_TOUCH
							GLOUTPUT(("Left Mouse move %d %d\n", x, y));
							#endif
							GameTap(game, GAME_TAP_MOVE, x, y, mod);
//							mouseMoveCount = 0;
						}
					}
					else if (!rightMouseDown.IsZero()) {
						GLASSERT(state & SDL_BUTTON(SDL_BUTTON_RIGHT));
						GameCameraPan(game, GAME_PAN_END, float(x), float(y));
					}
					else if (zooming && (state & SDL_BUTTON(SDL_BUTTON_RIGHT))) {
						float deltaZoom = 0.01f * (float)zoomY;
						GameZoom(game, GAME_ZOOM_DISTANCE, deltaZoom);
						GameCameraRotate(game, (float)(zoomX)*0.5f);
					}
					else if (state ==0) {
						GameTap(game, GAME_MOVE_WHILE_UP, x, y, mod);
					}
				}
				break;

				case SDL_MOUSEWHEEL:
				{
					if (event.wheel.y) {
						float deltaZoom = -0.1f * float(event.wheel.y);
						GameZoom(game, GAME_ZOOM_DISTANCE, deltaZoom);
					}
				}
				break;

				case SDL_FINGERUP:
				{
					const SDL_TouchFingerEvent* tfe = &event.tfinger;
					nFingers = SDL_GetNumTouchFingers(tfe->touchId);
					if (nFingers < 2 && !multiTouchStart.IsZero()) {
						#ifdef OUTPUT_MOUSE_AND_TOUCH
						GLOUTPUT(("2 finger END.\n"));
						#endif
						multiTouchStart.Zero();
					}
				}
				break;

				case SDL_FINGERDOWN:
				{
					const SDL_TouchFingerEvent* tfe = &event.tfinger;
					nFingers = SDL_GetNumTouchFingers(tfe->touchId);
					if (nFingers > 1) {
						if (!mouseDown.IsZero()) {
							// Wrap up the existing action.
							#ifdef OUTPUT_MOUSE_AND_TOUCH
							GLOUTPUT(("Switch to gesture.\n"));
							#endif
							mouseDown.Zero();
						}
					}
				}
				break;

				case SDL_MULTIGESTURE:
				{
					const SDL_MultiGestureEvent* mge = &event.mgesture;
					nFingers = SDL_GetNumTouchFingers(mge->touchId);
					if (nFingers > 1 && multiTouchStart.IsZero()) {
						#ifdef OUTPUT_MOUSE_AND_TOUCH
						GLOUTPUT(("2 finger START.\n"));
						#endif
						multiTouchStart.Set(mge->x, mge->y);
					}
					else if (!multiTouchStart.IsZero()) {
						// The Pan interacts badly with zoom and rotated. So instead of a continuous,
						// multi-event action do a bunch of "mini pans".
						GameCameraPan(game, GAME_PAN_START,
									  multiTouchStart.x * float(screenWidth), multiTouchStart.y*float(screenHeight));
						multiTouchStart.Set(mge->x, mge->y);
						GameCameraPan(game, GAME_PAN_MOVE,
									  multiTouchStart.x * float(screenWidth), multiTouchStart.y*float(screenHeight));
						GameCameraPan(game, GAME_PAN_END,
									  multiTouchStart.x * float(screenWidth), multiTouchStart.y*float(screenHeight));
					}

					if (nFingers == 2) {
						GameZoom(game, GAME_ZOOM_DISTANCE, -mge->dDist * 10.f);
						GameCameraRotate(game, -mge->dTheta * 100.0f);
						//GLOUTPUT(("MultiGestureEvent dTheta=%.4f dDist=%.4f x=%.4f y=%.4f nFing=%d\n", mge->dTheta, mge->dDist, mge->x, mge->y, mge->numFingers));
					}
				}
				break;

				case SDL_DOLLARGESTURE:
				{
					GLOUTPUT(("DollarGestureEvent\n"));
				}
				break;


				case SDL_QUIT:
				{
					done = true;
				}
				break;

				default:
				break;
			}
		}
		U32 delta = SDL_GetTicks() - tickTimer;
		if (delta < TIME_BETWEEN_FRAMES) {
			SDL_Delay(1);
			continue;
		}
		tickTimer = SDL_GetTicks();
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);

		const U8* keys = SDL_GetKeyboardState(0);
		U32 tickDelta = thisTick - lastTick;
		if (tickDelta > 100) tickDelta = 100;

		float keyMoveSpeed = KEY_MOVE_SPEED * float(tickDelta) / float(TIME_BETWEEN_FRAMES);
		float keyZoomSpeed = KEY_ZOOM_SPEED * float(tickDelta) / float(TIME_BETWEEN_FRAMES);
		float keyRotatepeed = KEY_ROTATE_SPEED * float(tickDelta) / float(TIME_BETWEEN_FRAMES);

		if (keys[SDL_SCANCODE_DOWN] || keys[SDL_SCANCODE_S]) {
			if (modKeys & KMOD_CTRL)
				GameZoom(game, GAME_ZOOM_DISTANCE, keyZoomSpeed);
			else
				GameCameraMove(game, 0, -keyMoveSpeed);
		}
		if (keys[SDL_SCANCODE_UP] || keys[SDL_SCANCODE_W]) {
			if (modKeys & KMOD_CTRL)
				GameZoom(game, GAME_ZOOM_DISTANCE, -keyZoomSpeed);
			else
				GameCameraMove(game, 0, keyMoveSpeed);
		}
		if (keys[SDL_SCANCODE_LEFT] || keys[SDL_SCANCODE_A]) {
			if (modKeys & KMOD_CTRL)
				GameCameraRotate(game, keyRotatepeed);
			else
				GameCameraMove(game, -keyMoveSpeed, 0);
		}
		if (keys[SDL_SCANCODE_RIGHT] || keys[SDL_SCANCODE_D]) {
			if (modKeys & KMOD_CTRL)
				GameCameraRotate(game, -keyRotatepeed);
			else
				GameCameraMove(game, keyMoveSpeed, 0);
		}

		if (game) {
			lastTick = thisTick;
			thisTick = SDL_GetTicks();
			PROFILE_BLOCK(GameDoTick);
			GameDoTick(game, thisTick);
		}
		{
			PROFILE_BLOCK(Swap);
			SDL_GL_SwapWindow(screen);
		}
	}

	GameSave(game);
	DeleteGame(game);

	for (int i = 0; i < nModDB; ++i) {
		delete databases[i];
	}

	SDL_Quit();

#if SEND_CRASH_LOGS
	// Empty the file - quit went just fine.
	{
		FILE* fp = FOpen( "UFO_Running.txt", "w" );
		if ( fp )
			fclose( fp );
	}
#endif

	MemLeakCheck();
	return 0;
}