/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" #include "application.h" #include "globals.h" #include "keyboardinput.h" #include "mouseinput.h" #include "gamepadinput.h" #include "console.h" #include "simulation.h" #include "world.h" #include "pyint.h" #include "sceneeditor.h" #include "renderer.h" #include "uilayer.h" #include "logs.h" #ifdef EU07_BUILD_STATIC #pragma comment( lib, "glfw3.lib" ) #pragma comment( lib, "glew32s.lib" ) #else #ifdef _WIN32 #pragma comment( lib, "glfw3dll.lib" ) #else #pragma comment( lib, "glfw3.lib" ) #endif #pragma comment( lib, "glew32.lib" ) #endif // build_static #pragma comment( lib, "opengl32.lib" ) #pragma comment( lib, "glu32.lib" ) #pragma comment( lib, "openal32.lib") #pragma comment( lib, "setupapi.lib" ) #pragma comment( lib, "python27.lib" ) #pragma comment( lib, "libserialport-0.lib" ) #pragma comment (lib, "dbghelp.lib") #pragma comment (lib, "version.lib") eu07_application Application; namespace input { gamepad_input Gamepad; mouse_input Mouse; glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode keyboard_input Keyboard; Console console; std::unique_ptr uart; user_command command; // currently issued control command, if any } #ifdef _WIN32 extern "C" { GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window ); } LONG CALLBACK unhandled_handler( ::EXCEPTION_POINTERS* e ); LRESULT APIENTRY WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); extern HWND Hwnd; extern WNDPROC BaseWindowProc; #endif // user input callbacks void window_resize_callback( GLFWwindow *window, int w, int h ) { // NOTE: we have two variables which basically do the same thing as we don't have dynamic fullscreen toggle // TBD, TODO: merge them? Global.iWindowWidth = w; Global.iWindowHeight = h; Global.fDistanceFactor = std::max( 0.5f, h / 768.0f ); // not sure if this is really something we want to use glViewport( 0, 0, w, h ); } void cursor_pos_callback( GLFWwindow *window, double x, double y ) { if( false == Global.ControlPicking ) { glfwSetCursorPos( window, 0, 0 ); } // give the potential event recipient a shot at it, in the virtual z order if( true == scene::Editor.on_mouse_move( x, y ) ) { return; } input::Mouse.move( x, y ); } void mouse_button_callback( GLFWwindow* window, int button, int action, int mods ) { if( ( button != GLFW_MOUSE_BUTTON_LEFT ) && ( button != GLFW_MOUSE_BUTTON_RIGHT ) ) { // we don't care about other mouse buttons at the moment return; } // give the potential event recipient a shot at it, in the virtual z order if( true == scene::Editor.on_mouse_button( button, action ) ) { return; } input::Mouse.button( button, action ); } void key_callback( GLFWwindow *window, int key, int scancode, int action, int mods ) { Global.shiftState = ( mods & GLFW_MOD_SHIFT ) ? true : false; Global.ctrlState = ( mods & GLFW_MOD_CONTROL ) ? true : false; Global.altState = ( mods & GLFW_MOD_ALT ) ? true : false; // give the ui first shot at the input processing... if( true == UILayer.on_key( key, action ) ) { return; } if( true == scene::Editor.on_key( key, action ) ) { return; } // ...if the input is left untouched, pass it on input::Keyboard.key( key, action ); if( ( true == Global.InputMouse ) && ( ( key == GLFW_KEY_LEFT_ALT ) || ( key == GLFW_KEY_RIGHT_ALT ) ) ) { // if the alt key was pressed toggle control picking mode and set matching cursor behaviour if( action == GLFW_RELEASE ) { if( Global.ControlPicking ) { // switch off Application.get_cursor_pos( input::mouse_pickmodepos.x, input::mouse_pickmodepos.y ); Application.set_cursor( GLFW_CURSOR_DISABLED ); Application.set_cursor_pos( 0, 0 ); } else { // enter picking mode Application.set_cursor_pos( input::mouse_pickmodepos.x, input::mouse_pickmodepos.y ); Application.set_cursor( GLFW_CURSOR_NORMAL ); } // actually toggle the mode Global.ControlPicking = !Global.ControlPicking; } } if( ( key == GLFW_KEY_LEFT_SHIFT ) || ( key == GLFW_KEY_LEFT_CONTROL ) || ( key == GLFW_KEY_LEFT_ALT ) || ( key == GLFW_KEY_RIGHT_SHIFT ) || ( key == GLFW_KEY_RIGHT_CONTROL ) || ( key == GLFW_KEY_RIGHT_ALT ) ) { // don't bother passing these return; } if( action == GLFW_PRESS || action == GLFW_REPEAT ) { World.OnKeyDown( key ); #ifdef CAN_I_HAS_LIBPNG switch( key ) { case GLFW_KEY_PRINT_SCREEN: { make_screenshot(); break; } default: { break; } } #endif } } void focus_callback( GLFWwindow *window, int focus ) { if( Global.bInactivePause ) // jeśli ma być pauzowanie okna w tle if( focus ) Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie else Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy } void scroll_callback( GLFWwindow* window, double xoffset, double yoffset ) { if( Global.ctrlState ) { // ctrl + scroll wheel adjusts fov in debug mode Global.FieldOfView = clamp( static_cast( Global.FieldOfView - yoffset * 20.0 / Global.fFpsAverage ), 15.0f, 75.0f ); } } // public: int eu07_application::init( int Argc, char *Argv[] ) { int result { 0 }; init_debug(); init_files(); if( ( result = init_settings( Argc, Argv ) ) != 0 ) { return result; } if( ( result = init_glfw() ) != 0 ) { return result; } init_callbacks(); if( ( result = init_gfx() ) != 0 ) { return result; } if( ( result = init_audio() ) != 0 ) { return result; } return result; } int eu07_application::run() { // HACK: prevent mouse capture before simulation starts Global.ControlPicking = true; // TODO: move input sources and their initializations to the application mode member input::Keyboard.init(); input::Mouse.init(); input::Gamepad.init(); if( true == Global.uart_conf.enable ) { input::uart = std::make_unique(); input::uart->init(); } #ifdef _WIN32 Console::On(); // włączenie konsoli #endif Global.pWorld = &World; // Ra: wskaźnik potrzebny do usuwania pojazdów if( false == World.Init( m_window ) ) { ErrorLog( "Bad init: simulation setup failed" ); return -1; } if( Global.iConvertModels < 0 ) { // generate binary files for all 3d models Global.iConvertModels = -Global.iConvertModels; World.CreateE3D( szModelPath ); // rekurencyjne przeglądanie katalogów World.CreateE3D( szDynamicPath, true ); // auto-close when you're done WriteLog( "Binary 3d model generation completed" ); return 0; } set_cursor( GLFW_CURSOR_DISABLED ); set_cursor_pos( 0, 0 ); Global.ControlPicking = false; // main application loop // TODO: split into parts and delegate these to application mode member while( ( false == glfwWindowShouldClose( m_window ) ) && ( true == World.Update() ) && ( true == GfxRenderer.Render() ) ) { glfwPollEvents(); input::Keyboard.poll(); if( true == Global.InputMouse ) { input::Mouse.poll(); } if( true == Global.InputGamepad ) { input::Gamepad.poll(); } if( input::uart != nullptr ) { input::uart->poll(); } // TODO: wrap current command in object, include other input sources input::command = ( input::Mouse.command() != user_command::none ? input::Mouse.command() : input::Keyboard.command() ); } return 0; } void eu07_application::exit() { #ifdef _WIN32 Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej) #endif SafeDelete( simulation::Region ); glfwDestroyWindow( m_window ); glfwTerminate(); TPythonInterpreter::killInstance(); } void eu07_application::set_cursor( int const Mode ) { UILayer.set_cursor( Mode ); } void eu07_application::set_cursor_pos( double const X, double const Y ) { if( m_window != nullptr ) { glfwSetCursorPos( m_window, X, Y ); } } void eu07_application::get_cursor_pos( double &X, double &Y ) const { if( m_window != nullptr ) { glfwGetCursorPos( m_window, &X, &Y ); } } // private: void eu07_application::init_debug() { #if defined(_MSC_VER) && defined (_DEBUG) // memory leaks _CrtSetDbgFlag( _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) | _CRTDBG_LEAK_CHECK_DF ); // floating point operation errors auto state { _clearfp() }; state = _control87( 0, 0 ); // this will turn on FPE for #IND and zerodiv state = _control87( state & ~( _EM_ZERODIVIDE | _EM_INVALID ), _MCW_EM ); #endif #ifdef _WIN32 ::SetUnhandledExceptionFilter( unhandled_handler ); #endif } void eu07_application::init_files() { #ifdef _WIN32 DeleteFile( "log.txt" ); DeleteFile( "errors.txt" ); _mkdir( "logs" ); #endif } int eu07_application::init_settings( int Argc, char *Argv[] ) { Global.LoadIniFile( "eu07.ini" ); if( ( Global.iWriteLogEnabled & 2 ) != 0 ) { // show output console if requested AllocConsole(); } /* std::string executable( argv[ 0 ] ); auto const pathend = executable.rfind( '\\' ); Global.ExecutableName = ( pathend != std::string::npos ? executable.substr( executable.rfind( '\\' ) + 1 ) : executable ); */ #ifdef _WIN32 // retrieve product version from the file's version data table { auto const fileversionsize { ::GetFileVersionInfoSize( Argv[ 0 ], NULL ) }; std::vectorfileversiondata; fileversiondata.resize( fileversionsize ); if( ::GetFileVersionInfo( Argv[ 0 ], 0, fileversionsize, fileversiondata.data() ) ) { struct lang_codepage { WORD language; WORD codepage; } *langcodepage; UINT datasize; ::VerQueryValue( fileversiondata.data(), TEXT( "\\VarFileInfo\\Translation" ), (LPVOID*)&langcodepage, &datasize ); std::string subblock; subblock.resize( 50 ); ::StringCchPrintf( &subblock[0], subblock.size(), TEXT( "\\StringFileInfo\\%04x%04x\\ProductVersion" ), langcodepage->language, langcodepage->codepage ); VOID *stringdata; if( ::VerQueryValue( fileversiondata.data(), subblock.data(), &stringdata, &datasize ) ) { Global.asVersion = std::string( reinterpret_cast(stringdata) ); } } } #endif // process command line arguments for( int i = 1; i < Argc; ++i ) { std::string token { Argv[ i ] }; if( token == "-e3d" ) { Global.iConvertModels = ( Global.iConvertModels > 0 ? -Global.iConvertModels : -7 ); // z optymalizacją, bananami i prawidłowym Opacity } else if( token == "-s" ) { if( i + 1 < Argc ) { Global.SceneryFile = ToLower( Argv[ ++i ] ); } } else if( token == "-v" ) { if( i + 1 < Argc ) { Global.asHumanCtrlVehicle = ToLower( Argv[ ++i ] ); } } else { std::cout << "usage: " << std::string( Argv[ 0 ] ) << " [-s sceneryfilepath]" << " [-v vehiclename]" << " [-e3d]" << std::endl; return -1; } } return 0; } int eu07_application::init_glfw() { if( glfwInit() == GLFW_FALSE ) { ErrorLog( "Bad init: failed to initialize glfw" ); return -1; } // match requested video mode to current to allow for // fullwindow creation when resolution is the same auto *monitor { glfwGetPrimaryMonitor() }; auto const *vmode { glfwGetVideoMode( monitor ) }; glfwWindowHint( GLFW_RED_BITS, vmode->redBits ); glfwWindowHint( GLFW_GREEN_BITS, vmode->greenBits ); glfwWindowHint( GLFW_BLUE_BITS, vmode->blueBits ); glfwWindowHint( GLFW_REFRESH_RATE, vmode->refreshRate ); glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE ); if( Global.iMultisampling > 0 ) { glfwWindowHint( GLFW_SAMPLES, 1 << Global.iMultisampling ); } if( Global.bFullScreen ) { // match screen dimensions with selected monitor, for 'borderless window' in fullscreen mode Global.iWindowWidth = vmode->width; Global.iWindowHeight = vmode->height; } auto *window { glfwCreateWindow( Global.iWindowWidth, Global.iWindowHeight, Global.AppName.c_str(), ( Global.bFullScreen ? monitor : nullptr ), nullptr ) }; if( window == nullptr ) { ErrorLog( "Bad init: failed to create glfw window" ); return -1; } glfwMakeContextCurrent( window ); glfwSwapInterval( Global.VSync ? 1 : 0 ); //vsync #ifdef _WIN32 // setup wrapper for base glfw window proc, to handle copydata messages Hwnd = glfwGetWin32Window( window ); BaseWindowProc = ( WNDPROC )::SetWindowLongPtr( Hwnd, GWLP_WNDPROC, (LONG_PTR)WndProc ); // switch off the topmost flag ::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); #endif // TBD, TODO: move the global pointer to a more appropriate place m_window = window; return 0; } void eu07_application::init_callbacks() { glfwSetFramebufferSizeCallback( m_window, window_resize_callback ); glfwSetCursorPosCallback( m_window, cursor_pos_callback ); glfwSetMouseButtonCallback( m_window, mouse_button_callback ); glfwSetKeyCallback( m_window, key_callback ); glfwSetScrollCallback( m_window, scroll_callback ); glfwSetWindowFocusCallback( m_window, focus_callback ); { int width, height; glfwGetFramebufferSize( m_window, &width, &height ); window_resize_callback( m_window, width, height ); } } int eu07_application::init_gfx() { if( glewInit() != GLEW_OK ) { ErrorLog( "Bad init: failed to initialize glew" ); return -1; } if( ( false == GfxRenderer.Init( m_window ) ) || ( false == UILayer.init( m_window ) ) ) { return -1; } return 0; } int eu07_application::init_audio() { if( Global.bSoundEnabled ) { Global.bSoundEnabled &= audio::renderer.init(); } // NOTE: lack of audio isn't deemed a failure serious enough to throw in the towel return 0; }