diff --git a/AnimModel.cpp b/AnimModel.cpp index 4676c08a..91b6372e 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "MdlMngr.h" #include "simulation.h" +#include "simulationtime.h" #include "Globals.h" #include "Timer.h" #include "Logs.h" diff --git a/AnimModel.h b/AnimModel.h index fb109893..626386ba 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -47,7 +47,7 @@ class TEvent; class TAnimContainer { // opakowanie submodelu, określające animację egzemplarza - obsługiwane jako lista - friend class TAnimModel; + friend TAnimModel; private: Math3D::vector3 vRotateAngles; // dla obrotów Eulera @@ -124,8 +124,8 @@ class TAnimAdvanced // opakowanie modelu, określające stan egzemplarza class TAnimModel : public scene::basic_node { - friend class opengl_renderer; - friend class ui_layer; + friend opengl_renderer; + friend ui_layer; public: // constructors diff --git a/Classes.h b/Classes.h index 202ac2aa..c175478d 100644 --- a/Classes.h +++ b/Classes.h @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- // Ra: zestaw klas do robienia wskaźników, aby uporządkować nagłówki //--------------------------------------------------------------------------- +class opengl_renderer; class TTrack; // odcinek trajektorii class TEvent; class TTrain; // pojazd sterowany @@ -29,8 +30,9 @@ class TTraction; // drut class TTractionPowerSource; // zasilanie drutów class TWorld; class TCamera; -class simulation_time; +class scenario_time; class TMoverParameters; +class ui_layer; namespace scene { struct node_data; diff --git a/Console.cpp b/Console.cpp index 5dc0cd3e..6de96f69 100644 --- a/Console.cpp +++ b/Console.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "Console.h" #include "Globals.h" +#include "application.h" #include "LPT.h" #include "Logs.h" #include "PoKeys55.h" @@ -247,7 +248,7 @@ void Console::BitsUpdate(int mask) bool Console::Pressed(int x) { // na razie tak - czyta się tylko klawiatura - if (glfwGetKey(Global.window, x) == GLFW_TRUE) + if (glfwGetKey(Application.window(), x) == GLFW_TRUE) return true; else return false; diff --git a/Driver.cpp b/Driver.cpp index 3ec513f1..b9ceee3d 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -22,10 +22,11 @@ http://mozilla.org/MPL/2.0/. #include "DynObj.h" #include "Event.h" #include "MemCell.h" -#include "World.h" -#include "utilities.h" -#include "McZapkie/MOVER.h" +#include "world.h" +#include "simulationtime.h" #include "track.h" +#include "station.h" +#include "utilities.h" #define LOGVELOCITY 0 #define LOGORDERS 1 diff --git a/DynObj.h b/DynObj.h index 0ce49ba3..15803796 100644 --- a/DynObj.h +++ b/DynObj.h @@ -157,7 +157,7 @@ struct material_data { class TDynamicObject { // klasa pojazdu - friend class opengl_renderer; + friend opengl_renderer; public: static bool bDynamicRemove; // moved from ground diff --git a/EU07.cpp b/EU07.cpp index c923cbf9..24f64e84 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -7,8 +7,8 @@ obtain one at http://mozilla.org/MPL/2.0/. */ /* - MaSzyna EU07 locomotive simulator - Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others +MaSzyna EU07 locomotive simulator +Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others */ /* Authors: @@ -16,475 +16,27 @@ MarcinW, McZapkie, Shaxbee, ABu, nbmx, youBy, Ra, winger, mamut, Q424, Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others */ #include "stdafx.h" -#ifdef CAN_I_HAS_LIBPNG -#include -#endif -#include "World.h" -#include "simulation.h" -#include "sceneeditor.h" -#include "Globals.h" -#include "timer.h" -#include "Logs.h" -#include "renderer.h" -#include "uilayer.h" -#include "audiorenderer.h" -#include "keyboardinput.h" -#include "mouseinput.h" -#include "gamepadinput.h" -#include "Console.h" -#include "uart.h" -#include "PyInt.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") -#ifdef CAN_I_HAS_LIBPNG -#pragma comment (lib, "libpng16.lib") -#endif +#include "application.h" +#include "logs.h" #ifdef _MSC_VER #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") #endif -TWorld World; +int main( int argc, char *argv[] ) { -namespace input { - -keyboard_input Keyboard; -mouse_input Mouse; -gamepad_input Gamepad; -glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode -std::unique_ptr uart; -user_command command; // currently issued control command, if any - -} - -#ifdef CAN_I_HAS_LIBPNG -void screenshot_save_thread( char *img ) -{ - png_image png; - memset(&png, 0, sizeof(png_image)); - png.version = PNG_IMAGE_VERSION; - png.width = Global.iWindowWidth; - png.height = Global.iWindowHeight; - png.format = PNG_FORMAT_RGB; - - char datetime[64]; - time_t timer; - struct tm* tm_info; - time(&timer); - tm_info = localtime(&timer); - strftime(datetime, 64, "%Y-%m-%d_%H-%M-%S", tm_info); - - uint64_t perf; - QueryPerformanceCounter((LARGE_INTEGER*)&perf); - - std::string filename = "screenshots/" + std::string(datetime) + - "_" + std::to_string(perf) + ".png"; - - if (png_image_write_to_file(&png, filename.c_str(), 0, img, -Global.iWindowWidth * 3, nullptr) == 1) - WriteLog("saved " + filename + "."); - else - WriteLog("failed to save screenshot."); - - delete[] img; -} - -void make_screenshot() -{ - char *img = new char[Global.iWindowWidth * Global.iWindowHeight * 3]; - glReadPixels(0, 0, Global.iWindowWidth, Global.iWindowHeight, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img); - - std::thread t(screenshot_save_thread, img); - t.detach(); -} -#endif - -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 - glfwGetCursorPos( window, &input::mouse_pickmodepos.x, &input::mouse_pickmodepos.y ); - UILayer.set_cursor( GLFW_CURSOR_DISABLED ); - glfwSetCursorPos( window, 0, 0 ); - } - else { - // enter picking mode - glfwSetCursorPos( window, input::mouse_pickmodepos.x, input::mouse_pickmodepos.y ); - UILayer.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 ); - } -} - -#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 - -int main(int argc, char *argv[]) -{ -#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 - - if (!glfwInit()) - return -1; - -#ifdef _WIN32 - DeleteFile( "log.txt" ); - DeleteFile( "errors.txt" ); - _mkdir("logs"); -#endif - Global.LoadIniFile("eu07.ini"); - - // hunter-271211: ukrywanie konsoli - if( Global.iWriteLogEnabled & 2 ) - { - AllocConsole(); - SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), FOREGROUND_GREEN ); - } -/* - std::string executable( argv[ 0 ] ); auto const pathend = executable.rfind( '\\' ); - Global.ExecutableName = - ( pathend != std::string::npos ? - executable.substr( executable.rfind( '\\' ) + 1 ) : - executable ); -*/ - // 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) ); - } - } - } - - for (int i = 1; i < argc; ++i) - { - std::string token(argv[i]); - - if (token == "-e3d") { - if (Global.iConvertModels > 0) - Global.iConvertModels = -Global.iConvertModels; - else - Global.iConvertModels = -7; // z optymalizacją, bananami i prawidłowym Opacity - } - else if (i + 1 < argc && token == "-s") - Global.SceneryFile = std::string(argv[++i]); - else if (i + 1 < argc && token == "-v") - { - std::string v(argv[++i]); - std::transform(v.begin(), v.end(), v.begin(), ::tolower); - Global.asHumanCtrlVehicle = v; - } - else - { - std::cout - << "usage: " << std::string(argv[0]) - << " [-s sceneryfilepath]" - << " [-v vehiclename]" - << " [-e3d]" - << std::endl; - return -1; - } - } - - // match requested video mode to current to allow for - // fullwindow creation when resolution is the same - GLFWmonitor *monitor = glfwGetPrimaryMonitor(); - const GLFWvidmode *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; - } - - GLFWwindow *window = - glfwCreateWindow( - Global.iWindowWidth, - Global.iWindowHeight, - Global.AppName.c_str(), - ( Global.bFullScreen ? - monitor : - nullptr), - nullptr ); - - if (!window) - { - std::cout << "failed to create window" << std::endl; - return -1; - } - glfwMakeContextCurrent(window); - glfwSwapInterval(Global.VSync ? 1 : 0); //vsync - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); //capture cursor - glfwSetCursorPos(window, 0.0, 0.0); - glfwSetFramebufferSizeCallback(window, window_resize_callback); - glfwSetCursorPosCallback(window, cursor_pos_callback); - glfwSetMouseButtonCallback( window, mouse_button_callback ); - glfwSetKeyCallback(window, key_callback); - glfwSetScrollCallback( window, scroll_callback ); - glfwSetWindowFocusCallback(window, focus_callback); - { - int width, height; - glfwGetFramebufferSize(window, &width, &height); - window_resize_callback(window, width, height); - } - - if (glewInit() != GLEW_OK) - { - std::cout << "failed to init GLEW" << std::endl; - return -1; - } - -#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 - - if( ( false == GfxRenderer.Init( window ) ) - || ( false == UILayer.init( window ) ) ) { - - return -1; - } - - if( Global.bSoundEnabled ) { - Global.bSoundEnabled &= audio::renderer.init(); - } - - input::Keyboard.init(); - input::Mouse.init(); - input::Gamepad.init(); - if( true == Global.uart_conf.enable ) { - input::uart = std::make_unique(); - input::uart->init(); - } - - Global.pWorld = &World; // Ra: wskaźnik potrzebny do usuwania pojazdów try { - if( false == World.Init( window ) ) { - ErrorLog( "Simulation setup failed" ); - return -1; + auto result { Application.init( argc, argv ) }; + if( result == 0 ) { + result = Application.run(); } + Application.exit(); + return result; } catch( std::bad_alloc const &Error ) { ErrorLog( "Critical error, memory allocation failure: " + std::string( Error.what() ) ); return -1; } -#ifdef _WIN32 - Console *pConsole = new Console(); // Ra: nie wiem, czy ma to sens, ale jakoś zainicjowac trzeba -#endif - if( Global.iConvertModels < 0 ) { - Global.iConvertModels = -Global.iConvertModels; - World.CreateE3D( szModelPath ); // rekurencyjne przeglądanie katalogów - World.CreateE3D( szDynamicPath, true ); - } // po zrobieniu E3D odpalamy normalnie scenerię, by ją zobaczyć -#ifdef _WIN32 - Console::On(); // włączenie konsoli -#endif - try { - while( ( false == glfwWindowShouldClose( 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() ); - } - } - catch( std::bad_alloc const &Error ) { - - ErrorLog( "Critical error, memory allocation failure: " + std::string( Error.what() ) ); - return -1; - } - -#ifdef _WIN32 - Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej) - SafeDelete( pConsole ); -#endif - SafeDelete( simulation::Region ); - - glfwDestroyWindow(window); - glfwTerminate(); - - TPythonInterpreter::killInstance(); - - return 0; } diff --git a/EvLaunch.cpp b/EvLaunch.cpp index c9bf882f..1d0ec0bb 100644 --- a/EvLaunch.cpp +++ b/EvLaunch.cpp @@ -23,7 +23,7 @@ http://mozilla.org/MPL/2.0/. #include "Timer.h" #include "parser.h" #include "Console.h" -#include "world.h" +#include "simulationtime.h" #include "utilities.h" //--------------------------------------------------------------------------- diff --git a/Globals.h b/Globals.h index b9af0d59..b924df68 100644 --- a/Globals.h +++ b/Globals.h @@ -20,7 +20,6 @@ struct global_settings { // members // data items // TODO: take these out of the settings - GLFWwindow *window{ nullptr }; bool shiftState{ false }; //m7todo: brzydko bool ctrlState{ false }; bool altState{ false }; diff --git a/Model3d.cpp b/Model3d.cpp index 2fe12510..37a26b42 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -20,6 +20,7 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others #include "utilities.h" #include "renderer.h" #include "Timer.h" +#include "simulationtime.h" #include "mtable.h" #include "sn_utils.h" //--------------------------------------------------------------------------- diff --git a/Model3d.h b/Model3d.h index 0b74e49a..7d6a8427 100644 --- a/Model3d.h +++ b/Model3d.h @@ -55,10 +55,10 @@ class TSubModel { // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów //m7todo: zrobić normalną serializację - friend class opengl_renderer; - friend class TModel3d; // temporary workaround. TODO: clean up class content/hierarchy - friend class TDynamicObject; // temporary etc - friend class scene::shape_node; // temporary etc + friend opengl_renderer; + friend TModel3d; // temporary workaround. TODO: clean up class content/hierarchy + friend TDynamicObject; // temporary etc + friend scene::shape_node; // temporary etc public: enum normalization { @@ -218,7 +218,7 @@ public: class TModel3d { - friend class opengl_renderer; + friend opengl_renderer; private: TSubModel *Root; // drzewo submodeli diff --git a/Track.h b/Track.h index 0f54aa4b..160fad5a 100644 --- a/Track.h +++ b/Track.h @@ -126,9 +126,9 @@ private: // trajektoria ruchu - opakowanie class TTrack : public scene::basic_node { - friend class opengl_renderer; + friend opengl_renderer; // NOTE: temporary arrangement - friend class ui_layer; + friend ui_layer; private: TIsolated * pIsolated = nullptr; // obwód izolowany obsługujący zajęcia/zwolnienia grupy torów diff --git a/Traction.h b/Traction.h index 120a7599..8a152127 100644 --- a/Traction.h +++ b/Traction.h @@ -20,7 +20,7 @@ class TTractionPowerSource; class TTraction : public scene::basic_node { - friend class opengl_renderer; + friend opengl_renderer; public: // na razie TTractionPowerSource *psPower[ 2 ] { nullptr, nullptr }; // najbliższe zasilacze z obu kierunków diff --git a/Train.cpp b/Train.cpp index 2aa14ed0..76c7fcfa 100644 --- a/Train.cpp +++ b/Train.cpp @@ -17,7 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "Globals.h" #include "simulation.h" -#include "world.h" +#include "simulationtime.h" #include "camera.h" #include "Logs.h" #include "MdlMngr.h" diff --git a/World.cpp b/World.cpp index dc1b6b5a..4c4b1452 100644 --- a/World.cpp +++ b/World.cpp @@ -16,7 +16,9 @@ http://mozilla.org/MPL/2.0/. #include "World.h" #include "Globals.h" +#include "application.h" #include "simulation.h" +#include "simulationtime.h" #include "Logs.h" #include "MdlMngr.h" #include "renderer.h" @@ -33,188 +35,13 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- -namespace simulation { - -simulation_time Time; - -basic_station Station; -} +TWorld World; extern "C" { GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window ); //m7todo: potrzebne do directsound } -void -simulation_time::init() { - - char monthdaycounts[ 2 ][ 13 ] = { - { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; - ::memcpy( m_monthdaycounts, monthdaycounts, sizeof( monthdaycounts ) ); - - // potentially adjust scenario clock - auto const requestedtime { clamp_circular( m_time.wHour * 60 + m_time.wMinute + Global.ScenarioTimeOffset * 60, 24 * 60 ) }; - auto const requestedhour { ( requestedtime / 60 ) % 24 }; - auto const requestedminute { requestedtime % 60 }; - // cache requested elements, if any - ::GetLocalTime( &m_time ); - - if( Global.fMoveLight > 0.0 ) { - // day and month of the year can be overriden by scenario setup - daymonth( m_time.wDay, m_time.wMonth, m_time.wYear, static_cast( Global.fMoveLight ) ); - } - - if( requestedhour != -1 ) { m_time.wHour = static_cast( clamp( requestedhour, 0, 23 ) ); } - if( requestedminute != -1 ) { m_time.wMinute = static_cast( clamp( requestedminute, 0, 59 ) ); } - // if the time is taken from the local clock leave the seconds intact, otherwise set them to zero - if( ( requestedhour != -1 ) - || ( requestedminute != 1 ) ) { - m_time.wSecond = 0; - } - - m_yearday = year_day( m_time.wDay, m_time.wMonth, m_time.wYear ); - - // calculate time zone bias - // retrieve relevant time zone info from system registry (or fall back on supplied default) - // TODO: select timezone matching defined geographic location and/or country - struct registry_time_zone_info { - long Bias; - long StandardBias; - long DaylightBias; - SYSTEMTIME StandardDate; - SYSTEMTIME DaylightDate; - } registrytimezoneinfo = { -60, 0, -60, { 0, 10, 0, 5, 3, 0, 0, 0 }, { 0, 3, 0, 5, 2, 0, 0, 0 } }; - -#ifdef _WIN32 - TCHAR timezonekeyname[] { TEXT( "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Central European Standard Time" ) }; - HKEY timezonekey { NULL }; - DWORD size { sizeof( registrytimezoneinfo ) }; - if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, timezonekeyname, 0, KEY_QUERY_VALUE, &timezonekey ) == ERROR_SUCCESS ) { - ::RegQueryValueEx( timezonekey, "TZI", NULL, NULL, (BYTE *)®istrytimezoneinfo, &size ); - } -#endif - TIME_ZONE_INFORMATION timezoneinfo { 0 }; - timezoneinfo.Bias = registrytimezoneinfo.Bias; - timezoneinfo.DaylightBias = registrytimezoneinfo.DaylightBias; - timezoneinfo.DaylightDate = registrytimezoneinfo.DaylightDate; - timezoneinfo.StandardBias = registrytimezoneinfo.StandardBias; - timezoneinfo.StandardDate = registrytimezoneinfo.StandardDate; - - auto zonebias { timezoneinfo.Bias }; - if( m_yearday < year_day( timezoneinfo.DaylightDate.wDay, timezoneinfo.DaylightDate.wMonth, m_time.wYear ) ) { - zonebias += timezoneinfo.StandardBias; - } - else if( m_yearday < year_day( timezoneinfo.StandardDate.wDay, timezoneinfo.StandardDate.wMonth, m_time.wYear ) ) { - zonebias += timezoneinfo.DaylightBias; - } - else { - zonebias += timezoneinfo.StandardBias; - } - - m_timezonebias = ( zonebias / 60.0 ); -} - -void -simulation_time::update( double const Deltatime ) { - - m_milliseconds += ( 1000.0 * Deltatime ); - while( m_milliseconds >= 1000.0 ) { - - ++m_time.wSecond; - m_milliseconds -= 1000.0; - } - m_time.wMilliseconds = std::floor( m_milliseconds ); - while( m_time.wSecond >= 60 ) { - - ++m_time.wMinute; - m_time.wSecond -= 60; - } - while( m_time.wMinute >= 60 ) { - - ++m_time.wHour; - m_time.wMinute -= 60; - } - while( m_time.wHour >= 24 ) { - - ++m_time.wDay; - ++m_time.wDayOfWeek; - if( m_time.wDayOfWeek >= 7 ) { - m_time.wDayOfWeek -= 7; - } - m_time.wHour -= 24; - } - int leap = ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ); - while( m_time.wDay > m_monthdaycounts[ leap ][ m_time.wMonth ] ) { - - m_time.wDay -= m_monthdaycounts[ leap ][ m_time.wMonth ]; - ++m_time.wMonth; - // unlikely but we might've entered a new year - if( m_time.wMonth > 12 ) { - - ++m_time.wYear; - leap = ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ); - m_time.wMonth -= 12; - } - } -} - -int -simulation_time::year_day( int Day, const int Month, const int Year ) const { - - char const daytab[ 2 ][ 13 ] = { - { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } - }; - - int leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; - for( int i = 1; i < Month; ++i ) - Day += daytab[ leap ][ i ]; - - return Day; -} - -void -simulation_time::daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday ) { - - WORD daytab[ 2 ][ 13 ] = { - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, - { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } - }; - - int leap = ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ); - WORD idx = 1; - while( ( idx < 13 ) && ( Yearday >= daytab[ leap ][ idx ] ) ) { - - ++idx; - } - Month = idx; - Day = Yearday - daytab[ leap ][ idx - 1 ]; -} - -int -simulation_time::julian_day() const { - - int yy = ( m_time.wYear < 0 ? m_time.wYear + 1 : m_time.wYear ) - std::floor( ( 12 - m_time.wMonth ) / 10.f ); - int mm = m_time.wMonth + 9; - if( mm >= 12 ) { mm -= 12; } - - int K1 = std::floor( 365.25 * ( yy + 4712 ) ); - int K2 = std::floor( 30.6 * mm + 0.5 ); - - // for dates in Julian calendar - int JD = K1 + K2 + m_time.wDay + 59; - // for dates in Gregorian calendar; 2299160 is October 15th, 1582 - const int gregorianswitchday = 2299160; - if( JD > gregorianswitchday ) { - - int K3 = std::floor( std::floor( ( yy * 0.01 ) + 49 ) * 0.75 ) - 38; - JD -= K3; - } - - return JD; -} - TWorld::TWorld() { Train = NULL; @@ -254,7 +81,6 @@ bool TWorld::Init( GLFWwindow *Window ) { auto timestart = std::chrono::system_clock::now(); window = Window; - Global.window = Window; // do WM_COPYDATA Global.pCamera = &Camera; // Ra: wskaźnik potrzebny do likwidacji drgań WriteLog( "\nStarting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")" ); @@ -935,7 +761,6 @@ bool TWorld::Update() { audio::renderer.update( dt ); GfxRenderer.Update( dt ); - ResourceSweep(); m_init = true; @@ -1007,12 +832,12 @@ TWorld::Update_Camera( double const Deltatime ) { // double modelrotate = atan2( -tempangle.x, tempangle.z ); if( ( true == Global.ctrlState ) - && ( ( glfwGetKey( Global.window, GLFW_KEY_LEFT ) == GLFW_TRUE ) - || ( glfwGetKey( Global.window, GLFW_KEY_RIGHT ) == GLFW_TRUE ) ) ) { + && ( ( glfwGetKey( Application.window(), GLFW_KEY_LEFT ) == GLFW_TRUE ) + || ( glfwGetKey( Application.window(), GLFW_KEY_RIGHT ) == GLFW_TRUE ) ) ) { // jeśli lusterko lewe albo prawe (bez rzucania na razie) Global.CabWindowOpen = true; - auto const lr { glfwGetKey( Global.window, GLFW_KEY_LEFT ) == GLFW_TRUE }; + auto const lr { glfwGetKey( Application.window(), GLFW_KEY_LEFT ) == GLFW_TRUE }; // Camera.Yaw powinno być wyzerowane, aby po powrocie patrzeć do przodu Camera.Pos = Controlled->GetPosition() + Train->MirrorPosition( lr ); // pozycja lusterka Camera.Yaw = 0; // odchylenie na bok od Camera.LookAt @@ -1079,13 +904,6 @@ void TWorld::Update_Environment() { Environment.update(); } -void TWorld::ResourceSweep() -{ -/* - ResourceManager::Sweep( Timer::GetSimulationTime() ); -*/ -}; - //--------------------------------------------------------------------------- void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) { // odebranie komunikatu z serwera @@ -1445,11 +1263,11 @@ TWorld::compute_season( int const Yearday ) const { using dayseasonpair = std::pair; std::vector seasonsequence { - { 65, "winter" }, - { 158, "spring" }, - { 252, "summer" }, - { 341, "autumn" }, - { 366, "winter" } }; + { 65, "winter:" }, + { 158, "spring:" }, + { 252, "summer:" }, + { 341, "autumn:" }, + { 366, "winter:" } }; auto const lookup = std::lower_bound( std::begin( seasonsequence ), std::end( seasonsequence ), @@ -1457,7 +1275,7 @@ TWorld::compute_season( int const Yearday ) const { []( dayseasonpair const &Left, const int Right ) { return Left.first < Right; } ); - Global.Season = lookup->second + ":"; + Global.Season = lookup->second; // season can affect the weather so if it changes, re-calculate weather as well compute_weather(); } diff --git a/World.h b/World.h index 731af207..c0131762 100644 --- a/World.h +++ b/World.h @@ -20,52 +20,6 @@ http://mozilla.org/MPL/2.0/. #include "stars.h" #include "skydome.h" #include "messaging.h" -#include "station.h" - -// wrapper for simulation time -class simulation_time { - -public: - simulation_time() { m_time.wHour = 10; m_time.wMinute = 30; } - void - init(); - void - update( double const Deltatime ); - SYSTEMTIME & - data() { return m_time; } - SYSTEMTIME const & - data() const { return m_time; } - double - second() const { return ( m_time.wMilliseconds * 0.001 + m_time.wSecond ); } - int - year_day() const { return m_yearday; } - // helper, calculates day of year from given date - int - year_day( int Day, int const Month, int const Year ) const; - int - julian_day() const; - double - zone_bias() const { return m_timezonebias; } - -private: - // calculates day and month from given day of year - void - daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday ); - - SYSTEMTIME m_time; - double m_milliseconds{ 0.0 }; - int m_yearday; - char m_monthdaycounts[ 2 ][ 13 ]; - double m_timezonebias{ 0.0 }; -}; - -namespace simulation { - -extern simulation_time Time; - -extern basic_station Station; // temporary object, for station functionality tests - -} // wrapper for environment elements -- sky, sun, stars, clouds etc class world_environment { @@ -126,7 +80,6 @@ TWorld(); private: void Update_Environment(); void Update_Camera( const double Deltatime ); - void ResourceSweep(); // handles vehicle change flag void ChangeDynamic(); void InOutKey( bool const Near = true ); @@ -154,5 +107,7 @@ private: GLFWwindow *window; }; +extern TWorld World; + //--------------------------------------------------------------------------- diff --git a/application.cpp b/application.cpp new file mode 100644 index 00000000..0d409929 --- /dev/null +++ b/application.cpp @@ -0,0 +1,514 @@ +/* +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() { + + // 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 ); + + // 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 = Argv[ ++i ]; + } + } + else if( token == "-v" ) { + if( i + 1 < Argc ) { + Global.asHumanCtrlVehicle = 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; +} diff --git a/application.h b/application.h new file mode 100644 index 00000000..719f8ade --- /dev/null +++ b/application.h @@ -0,0 +1,46 @@ +/* +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/. +*/ + +class eu07_application { + +public: +// constructors + eu07_application() = default; +// methods + int + init( int Argc, char *Argv[] ); + int + run(); + void + exit(); + void + set_cursor( int const Mode ); + void + set_cursor_pos( double const X, double const Y ); + void + get_cursor_pos( double &X, double &Y ) const; + inline + GLFWwindow * + window() { + return m_window; } + +private: +// methods + void init_debug(); + void init_files(); + int init_settings( int Argc, char *Argv[] ); + int init_glfw(); + void init_callbacks(); + int init_gfx(); + int init_audio(); +// members + GLFWwindow * m_window { nullptr }; +}; + +extern eu07_application Application; \ No newline at end of file diff --git a/audiorenderer.h b/audiorenderer.h index 89b81784..d0a89eb0 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "resourcemanager.h" #include "uitranscripts.h" +class opengl_renderer; class sound_source; using uint32_sequence = std::vector; @@ -100,7 +101,7 @@ private: class openal_renderer { - friend class opengl_renderer; + friend opengl_renderer; public: // constructors diff --git a/keyboardinput.cpp b/keyboardinput.cpp index 63b9897f..b68326f8 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -12,9 +12,6 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "logs.h" #include "parser.h" -#include "world.h" - -extern TWorld World; bool keyboard_input::recall_bindings() { diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index 84f19a08..f18bbee1 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -51,9 +51,6 @@ Source Files - - Source Files - Source Files @@ -246,6 +243,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -476,6 +482,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/messaging.cpp b/messaging.cpp index f651d6c4..6dbc78bd 100644 --- a/messaging.cpp +++ b/messaging.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "messaging.h" #include "globals.h" +#include "application.h" #include "simulation.h" #include "mtable.h" #include "logs.h" @@ -51,7 +52,7 @@ WyslijEvent(const std::string &e, const std::string &d) cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura cData.cbData = (DWORD)(12 + i + j); // 8+dwa liczniki i dwa zera kończące cData.lpData = &r; - Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global.window ), (LPARAM)&cData ); + Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Application.window() ), (LPARAM)&cData ); CommLog( Now() + " " + std::to_string(r.iComm) + " " + e + " sent" ); #endif } @@ -71,7 +72,7 @@ WyslijUszkodzenia(const std::string &t, char fl) cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura cData.cbData = (DWORD)(11 + i); // 8+licznik i zero kończące cData.lpData = &r; - Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global.window ), (LPARAM)&cData ); + Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Application.window() ), (LPARAM)&cData ); CommLog( Now() + " " + std::to_string(r.iComm) + " " + t + " sent"); #endif } @@ -90,7 +91,7 @@ WyslijString(const std::string &t, int n) cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura cData.cbData = (DWORD)(10 + i); // 8+licznik i zero kończące cData.lpData = &r; - Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global.window ), (LPARAM)&cData ); + Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Application.window() ), (LPARAM)&cData ); CommLog( Now() + " " + std::to_string(r.iComm) + " " + t + " sent"); #endif } @@ -174,7 +175,7 @@ WyslijNamiary(TDynamicObject const *Vehicle) cData.cbData = (DWORD)(10 + i + j); // 8+licznik i zero kończące cData.lpData = &r; // WriteLog("Ramka gotowa"); - Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global.window ), (LPARAM)&cData ); + Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Application.window() ), (LPARAM)&cData ); // WriteLog("Ramka poszla!"); CommLog( Now() + " " + std::to_string(r.iComm) + " " + Vehicle->asName + " sent"); #endif @@ -223,7 +224,7 @@ WyslijObsadzone() cData.cbData = 8 + 1984; // 8+licznik i zero kończące cData.lpData = &r; // WriteLog("Ramka gotowa"); - Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global.window ), (LPARAM)&cData ); + Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Application.window() ), (LPARAM)&cData ); CommLog( Now() + " " + std::to_string(r.iComm) + " obsadzone" + " sent"); #endif } @@ -249,7 +250,7 @@ WyslijParam(int nr, int fl) cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura cData.cbData = 12 + i; // 12+rozmiar danych cData.lpData = &r; - Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global.window ), (LPARAM)&cData ); + Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Application.window() ), (LPARAM)&cData ); #endif } diff --git a/moon.cpp b/moon.cpp index b40d5356..e28a13e7 100644 --- a/moon.cpp +++ b/moon.cpp @@ -4,7 +4,7 @@ #include "globals.h" #include "mtable.h" #include "utilities.h" -#include "world.h" +#include "simulationtime.h" ////////////////////////////////////////////////////////////////////////////////////////// // cSun -- class responsible for dynamic calculation of position and intensity of the Sun, diff --git a/mouseinput.cpp b/mouseinput.cpp index af45af26..cc806416 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -9,6 +9,8 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "mouseinput.h" + +#include "application.h" #include "utilities.h" #include "globals.h" #include "timer.h" @@ -18,8 +20,6 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "uilayer.h" -extern TWorld World; - void mouse_slider::bind( user_command const &Command ) { @@ -82,15 +82,14 @@ mouse_slider::bind( user_command const &Command ) { } } // hide the cursor and place it in accordance with current slider value - glfwGetCursorPos( Global.window, &m_cursorposition.x, &m_cursorposition.y ); - UILayer.set_cursor( GLFW_CURSOR_DISABLED ); + Application.get_cursor_pos( m_cursorposition.x, m_cursorposition.y ); + Application.set_cursor( GLFW_CURSOR_DISABLED ); auto const controlsize { Global.iWindowHeight * 0.75 }; auto const controledge { Global.iWindowHeight * 0.5 + controlsize * 0.5 }; auto const stepsize { controlsize / m_valuerange }; - glfwSetCursorPos( - Global.window, + Application.set_cursor_pos( Global.iWindowWidth * 0.5, ( m_analogue ? controledge - ( 1.0 - m_value ) * controlsize : @@ -101,8 +100,8 @@ void mouse_slider::release() { m_command = user_command::none; - glfwSetCursorPos( Global.window, m_cursorposition.x, m_cursorposition.y ); - UILayer.set_cursor( GLFW_CURSOR_NORMAL ); + Application.set_cursor_pos( m_cursorposition.x, m_cursorposition.y ); + Application.set_cursor( GLFW_CURSOR_NORMAL ); } void @@ -525,10 +524,10 @@ mouse_input::default_bindings() { user_command::pantographtogglerear, user_command::none } }, { "pantfrontoff_sw:", { - user_command::pantographtogglefront, + user_command::pantographlowerfront, user_command::none } }, // TODO: dedicated lower pantograph commands { "pantrearoff_sw:", { - user_command::pantographtogglerear, + user_command::pantographlowerrear, user_command::none } }, // TODO: dedicated lower pantograph commands { "pantalloff_sw:", { user_command::pantographlowerall, diff --git a/mtable.cpp b/mtable.cpp index b92d10b4..1e96c458 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -11,7 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "mtable.h" #include "globals.h" -#include "world.h" +#include "simulationtime.h" #include "utilities.h" double TTrainParameters::CheckTrainLatency() @@ -49,7 +49,7 @@ bool TTrainParameters::IsStop() return true; // na ostatnim się zatrzymać zawsze } -bool TTrainParameters::UpdateMTable( simulation_time const &Time, std::string const &NewName ) { +bool TTrainParameters::UpdateMTable( scenario_time const &Time, std::string const &NewName ) { return UpdateMTable( Time.data().wHour, Time.data().wMinute, NewName ); } diff --git a/mtable.h b/mtable.h index abdb9449..30adc18e 100644 --- a/mtable.h +++ b/mtable.h @@ -75,7 +75,7 @@ class TTrainParameters bool IsStop(); bool IsTimeToGo(double hh, double mm); bool UpdateMTable(double hh, double mm, std::string const &NewName); - bool UpdateMTable( simulation_time const &Time, std::string const &NewName ); + bool UpdateMTable( scenario_time const &Time, std::string const &NewName ); bool RewindTimeTable( std::string actualStationName ); TTrainParameters( std::string const &NewTrainName ); void NewName(std::string const &NewTrainName); diff --git a/renderer.cpp b/renderer.cpp index a27fbb66..0be13091 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "timer.h" #include "simulation.h" +#include "simulationtime.h" #include "world.h" #include "train.h" #include "dynobj.h" @@ -24,7 +25,6 @@ http://mozilla.org/MPL/2.0/. #include "utilities.h" opengl_renderer GfxRenderer; -extern TWorld World; int const EU07_PICKBUFFERSIZE { 1024 }; // size of (square) textures bound with the pick framebuffer int const EU07_ENVIRONMENTBUFFERSIZE { 256 }; // size of (square) environmental cube map texture diff --git a/scene.h b/scene.h index 78ebfbe6..3dc886a3 100644 --- a/scene.h +++ b/scene.h @@ -60,7 +60,7 @@ struct scratch_data { // TBD, TODO: replace with quadtree scheme? class basic_cell { - friend class opengl_renderer; + friend opengl_renderer; public: // constructors @@ -198,7 +198,7 @@ private: // basic scene partitioning structure, holds terrain geometry and collection of cells class basic_section { - friend class opengl_renderer; + friend opengl_renderer; public: // constructors @@ -302,7 +302,7 @@ private: // top-level of scene spatial structure, holds collection of sections class basic_region { - friend class opengl_renderer; + friend opengl_renderer; public: // constructors diff --git a/sceneeditor.cpp b/sceneeditor.cpp index c8144244..3042196c 100644 --- a/sceneeditor.cpp +++ b/sceneeditor.cpp @@ -11,8 +11,8 @@ http://mozilla.org/MPL/2.0/. #include "sceneeditor.h" #include "globals.h" +#include "application.h" #include "simulation.h" -#include "uilayer.h" #include "renderer.h" namespace scene { @@ -47,7 +47,7 @@ basic_editor::on_mouse_button( int const Button, int const Action ) { m_node = GfxRenderer.Update_Pick_Node(); m_nodesnapshot = { m_node }; if( m_node ) { - UILayer.set_cursor( GLFW_CURSOR_DISABLED ); + Application.set_cursor( GLFW_CURSOR_DISABLED ); } } else { @@ -55,7 +55,7 @@ basic_editor::on_mouse_button( int const Button, int const Action ) { // TODO: record the current undo step on the undo stack m_nodesnapshot = { m_node }; if( m_node ) { - UILayer.set_cursor( GLFW_CURSOR_NORMAL ); + Application.set_cursor( GLFW_CURSOR_NORMAL ); } } diff --git a/simulation.cpp b/simulation.cpp index e5a7dd65..a3a076f4 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -10,11 +10,11 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "simulation.h" -#include "world.h" #include "globals.h" -#include "logs.h" +#include "simulationtime.h" #include "uilayer.h" #include "renderer.h" +#include "logs.h" namespace simulation { @@ -32,9 +32,42 @@ light_array Lights; scene::basic_region *Region { nullptr }; + + bool state_manager::deserialize( std::string const &Scenariofile ) { + return m_serializer.deserialize( Scenariofile ); +} + +// stores class data in specified file, in legacy (text) format +void +state_manager::export_as_text( std::string const &Scenariofile ) const { + + return m_serializer.export_as_text( Scenariofile ); +} + +// legacy method, calculates changes in simulation state over specified time +void +state_manager::update( double const Deltatime, int Iterationcount ) { + // aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń + if (Deltatime == 0.0) { + return; + } + + auto const totaltime { Deltatime * Iterationcount }; + // NOTE: we perform animations first, as they can determine factors like contact with powergrid + TAnimModel::AnimUpdate( totaltime ); // wykonanie zakolejkowanych animacji + + simulation::Powergrid.update( totaltime ); + simulation::Vehicles.update( Deltatime, Iterationcount ); +} + + + +bool +state_serializer::deserialize( std::string const &Scenariofile ) { + // TODO: move initialization to separate routine so we can reuse it SafeDelete( Region ); Region = new scene::basic_region(); @@ -61,50 +94,33 @@ state_manager::deserialize( std::string const &Scenariofile ) { return true; } -// legacy method, calculates changes in simulation state over specified time -void -state_manager::update( double const Deltatime, int Iterationcount ) { - // aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń - if (Deltatime == 0.0) { - // jeśli załączona jest pauza, to tylko obsłużyć ruch w kabinie trzeba - return; - } - - auto const totaltime { Deltatime * Iterationcount }; - // NOTE: we perform animations first, as they can determine factors like contact with powergrid - TAnimModel::AnimUpdate( totaltime ); // wykonanie zakolejkowanych animacji - - simulation::Powergrid.update( totaltime ); - simulation::Vehicles.update( Deltatime, Iterationcount ); -} - // restores class data from provided stream void -state_manager::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { // prepare deserialization function table // since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task - using deserializefunction = void(state_manager::*)(cParser &, scene::scratch_data &); + using deserializefunction = void( state_serializer::*)(cParser &, scene::scratch_data &); std::vector< std::pair< std::string, deserializefunction> > functionlist = { - { "atmo", &state_manager::deserialize_atmo }, - { "camera", &state_manager::deserialize_camera }, - { "config", &state_manager::deserialize_config }, - { "description", &state_manager::deserialize_description }, - { "event", &state_manager::deserialize_event }, - { "firstinit", &state_manager::deserialize_firstinit }, - { "light", &state_manager::deserialize_light }, - { "node", &state_manager::deserialize_node }, - { "origin", &state_manager::deserialize_origin }, - { "endorigin", &state_manager::deserialize_endorigin }, - { "rotate", &state_manager::deserialize_rotate }, - { "sky", &state_manager::deserialize_sky }, - { "test", &state_manager::deserialize_test }, - { "time", &state_manager::deserialize_time }, - { "trainset", &state_manager::deserialize_trainset }, - { "endtrainset", &state_manager::deserialize_endtrainset } }; + { "atmo", &state_serializer::deserialize_atmo }, + { "camera", &state_serializer::deserialize_camera }, + { "config", &state_serializer::deserialize_config }, + { "description", &state_serializer::deserialize_description }, + { "event", &state_serializer::deserialize_event }, + { "firstinit", &state_serializer::deserialize_firstinit }, + { "light", &state_serializer::deserialize_light }, + { "node", &state_serializer::deserialize_node }, + { "origin", &state_serializer::deserialize_origin }, + { "endorigin", &state_serializer::deserialize_endorigin }, + { "rotate", &state_serializer::deserialize_rotate }, + { "sky", &state_serializer::deserialize_sky }, + { "test", &state_serializer::deserialize_test }, + { "time", &state_serializer::deserialize_time }, + { "trainset", &state_serializer::deserialize_trainset }, + { "endtrainset", &state_serializer::deserialize_endtrainset } }; using deserializefunctionbind = std::function; std::unordered_map< std::string, @@ -146,7 +162,7 @@ state_manager::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { } void -state_manager::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) { // NOTE: parameter system needs some decent replacement, but not worth the effort if we're moving to built-in editor // atmosphere color; legacy parameter, no longer used @@ -178,7 +194,7 @@ state_manager::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad } void -state_manager::deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ) { glm::dvec3 xyz, abc; int i = -1, into = -1; // do której definicji kamery wstawić @@ -232,21 +248,21 @@ state_manager::deserialize_camera( cParser &Input, scene::scratch_data &Scratchp } void -state_manager::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) { // config parameters (re)definition Global.ConfigParse( Input ); } void -state_manager::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) { // legacy section, never really used; skip_until( Input, "enddescription" ); } void -state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ) { // TODO: refactor event class and its de/serialization. do offset and rotation after deserialization is done auto *event = new TEvent(); @@ -265,7 +281,7 @@ state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpa } void -state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { if( true == Scratchpad.initialized ) { return; } @@ -279,14 +295,14 @@ state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scrat } void -state_manager::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) { // legacy section, no longer used nor supported; skip_until( Input, "endlight" ); } void -state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) { auto const inputline = Input.Line(); // cache in case we need to report error @@ -469,7 +485,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } void -state_manager::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) { glm::dvec3 offset; Input.getTokens( 3 ); @@ -486,7 +502,7 @@ state_manager::deserialize_origin( cParser &Input, scene::scratch_data &Scratchp } void -state_manager::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) { if( false == Scratchpad.location.offset.empty() ) { Scratchpad.location.offset.pop(); @@ -497,7 +513,7 @@ state_manager::deserialize_endorigin( cParser &Input, scene::scratch_data &Scrat } void -state_manager::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) { Input.getTokens( 3 ); Input @@ -507,7 +523,7 @@ state_manager::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchp } void -state_manager::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) { // sky model Input.getTokens( 1 ); @@ -518,14 +534,14 @@ state_manager::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad } void -state_manager::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) { // legacy section, no longer supported; skip_until( Input, "endtest" ); } void -state_manager::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) { // current scenario time cParser timeparser( Input.getToken() ); @@ -548,7 +564,7 @@ state_manager::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad } void -state_manager::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) { if( true == Scratchpad.trainset.is_open ) { // shouldn't happen but if it does wrap up currently open trainset and report an error @@ -568,7 +584,7 @@ state_manager::deserialize_trainset( cParser &Input, scene::scratch_data &Scratc } void -state_manager::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) { +state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) { if( ( false == Scratchpad.trainset.is_open ) || ( true == Scratchpad.trainset.vehicles.empty() ) ) { @@ -616,7 +632,7 @@ state_manager::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scr // creates path and its wrapper, restoring class data from provided stream TTrack * -state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { // TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done auto *track = new TTrack( Nodedata ); @@ -633,7 +649,7 @@ state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad } TTraction * -state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { if( false == Global.bLoadTraction ) { skip_until( Input, "endtraction" ); @@ -651,7 +667,7 @@ state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratc } TTractionPowerSource * -state_manager::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { if( false == Global.bLoadTraction ) { skip_until( Input, "end" ); @@ -667,7 +683,7 @@ state_manager::deserialize_tractionpowersource( cParser &Input, scene::scratch_d } TMemCell * -state_manager::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { auto *memorycell = new TMemCell( Nodedata ); memorycell->Load( &Input ); @@ -678,7 +694,7 @@ state_manager::deserialize_memorycell( cParser &Input, scene::scratch_data &Scra } TEventLauncher * -state_manager::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { glm::dvec3 location; Input.getTokens( 3 ); @@ -695,7 +711,7 @@ state_manager::deserialize_eventlauncher( cParser &Input, scene::scratch_data &S } TAnimModel * -state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { glm::dvec3 location; glm::vec3 rotation; @@ -721,7 +737,7 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa } TDynamicObject * -state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { if( false == Scratchpad.trainset.is_open ) { // part of trainset data is used when loading standalone vehicles, so clear it just in case @@ -846,7 +862,7 @@ state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratch } sound_source * -state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { +state_serializer::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { glm::dvec3 location; Input.getTokens( 3 ); @@ -869,7 +885,7 @@ state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpa // skips content of stream until specified token void -state_manager::skip_until( cParser &Input, std::string const &Token ) { +state_serializer::skip_until( cParser &Input, std::string const &Token ) { std::string token { Input.getToken() }; while( ( false == token.empty() ) @@ -881,7 +897,7 @@ state_manager::skip_until( cParser &Input, std::string const &Token ) { // transforms provided location by specifed rotation and offset glm::dvec3 -state_manager::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) { +state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) { if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { auto const rotation = glm::radians( Scratchpad.location.rotation ); @@ -896,7 +912,7 @@ state_manager::transform( glm::dvec3 Location, scene::scratch_data const &Scratc // stores class data in specified file, in legacy (text) format void -state_manager::export_as_text( std::string const &Scenariofile ) const { +state_serializer::export_as_text( std::string const &Scenariofile ) const { if( Scenariofile == "$.scn" ) { ErrorLog( "Bad file: scenery export not supported for file \"$.scn\"" ); diff --git a/simulation.h b/simulation.h index 91f3785f..3e3a35f0 100644 --- a/simulation.h +++ b/simulation.h @@ -25,15 +25,11 @@ http://mozilla.org/MPL/2.0/. namespace simulation { -class state_manager { +class state_serializer { public: -// types - // methods - // legacy method, calculates changes in simulation state over specified time - void - update( double Deltatime, int Iterationcount ); + // restores simulation data from specified file. returns: true on success, false otherwise bool deserialize( std::string const &Scenariofile ); // stores class data in specified file, in legacy (text) format @@ -72,7 +68,25 @@ private: void skip_until( cParser &Input, std::string const &Token ); // transforms provided location by specifed rotation and offset glm::dvec3 transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ); +}; +class state_manager { + +public: +// methods + // legacy method, calculates changes in simulation state over specified time + void + update( double Deltatime, int Iterationcount ); + // restores simulation data from specified file. returns: true on success, false otherwise + bool + deserialize( std::string const &Scenariofile ); + // stores class data in specified file, in legacy (text) format + void + export_as_text( std::string const &Scenariofile ) const; + +private: +// members + state_serializer m_serializer; }; extern state_manager State; diff --git a/simulationtime.cpp b/simulationtime.cpp new file mode 100644 index 00000000..80994b5e --- /dev/null +++ b/simulationtime.cpp @@ -0,0 +1,192 @@ +/* +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 "simulationtime.h" + +#include "globals.h" +#include "utilities.h" + +namespace simulation { + +scenario_time Time; + +} // simulation + +void +scenario_time::init() { + + char monthdaycounts[ 2 ][ 13 ] = { + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; + ::memcpy( m_monthdaycounts, monthdaycounts, sizeof( monthdaycounts ) ); + + // potentially adjust scenario clock + auto const requestedtime { clamp_circular( m_time.wHour * 60 + m_time.wMinute + Global.ScenarioTimeOffset * 60, 24 * 60 ) }; + auto const requestedhour { ( requestedtime / 60 ) % 24 }; + auto const requestedminute { requestedtime % 60 }; + // cache requested elements, if any + ::GetLocalTime( &m_time ); + + if( Global.fMoveLight > 0.0 ) { + // day and month of the year can be overriden by scenario setup + daymonth( m_time.wDay, m_time.wMonth, m_time.wYear, static_cast( Global.fMoveLight ) ); + } + + if( requestedhour != -1 ) { m_time.wHour = static_cast( clamp( requestedhour, 0, 23 ) ); } + if( requestedminute != -1 ) { m_time.wMinute = static_cast( clamp( requestedminute, 0, 59 ) ); } + // if the time is taken from the local clock leave the seconds intact, otherwise set them to zero + if( ( requestedhour != -1 ) + || ( requestedminute != 1 ) ) { + m_time.wSecond = 0; + } + + m_yearday = year_day( m_time.wDay, m_time.wMonth, m_time.wYear ); + + // calculate time zone bias + // retrieve relevant time zone info from system registry (or fall back on supplied default) + // TODO: select timezone matching defined geographic location and/or country + struct registry_time_zone_info { + long Bias; + long StandardBias; + long DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; + } registrytimezoneinfo = { -60, 0, -60, { 0, 10, 0, 5, 3, 0, 0, 0 }, { 0, 3, 0, 5, 2, 0, 0, 0 } }; + +#ifdef _WIN32 + TCHAR timezonekeyname[] { TEXT( "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Central European Standard Time" ) }; + HKEY timezonekey { NULL }; + DWORD size { sizeof( registrytimezoneinfo ) }; + if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, timezonekeyname, 0, KEY_QUERY_VALUE, &timezonekey ) == ERROR_SUCCESS ) { + ::RegQueryValueEx( timezonekey, "TZI", NULL, NULL, (BYTE *)®istrytimezoneinfo, &size ); + } +#endif + TIME_ZONE_INFORMATION timezoneinfo { 0 }; + timezoneinfo.Bias = registrytimezoneinfo.Bias; + timezoneinfo.DaylightBias = registrytimezoneinfo.DaylightBias; + timezoneinfo.DaylightDate = registrytimezoneinfo.DaylightDate; + timezoneinfo.StandardBias = registrytimezoneinfo.StandardBias; + timezoneinfo.StandardDate = registrytimezoneinfo.StandardDate; + + auto zonebias { timezoneinfo.Bias }; + if( m_yearday < year_day( timezoneinfo.DaylightDate.wDay, timezoneinfo.DaylightDate.wMonth, m_time.wYear ) ) { + zonebias += timezoneinfo.StandardBias; + } + else if( m_yearday < year_day( timezoneinfo.StandardDate.wDay, timezoneinfo.StandardDate.wMonth, m_time.wYear ) ) { + zonebias += timezoneinfo.DaylightBias; + } + else { + zonebias += timezoneinfo.StandardBias; + } + + m_timezonebias = ( zonebias / 60.0 ); +} + +void +scenario_time::update( double const Deltatime ) { + + m_milliseconds += ( 1000.0 * Deltatime ); + while( m_milliseconds >= 1000.0 ) { + + ++m_time.wSecond; + m_milliseconds -= 1000.0; + } + m_time.wMilliseconds = std::floor( m_milliseconds ); + while( m_time.wSecond >= 60 ) { + + ++m_time.wMinute; + m_time.wSecond -= 60; + } + while( m_time.wMinute >= 60 ) { + + ++m_time.wHour; + m_time.wMinute -= 60; + } + while( m_time.wHour >= 24 ) { + + ++m_time.wDay; + ++m_time.wDayOfWeek; + if( m_time.wDayOfWeek >= 7 ) { + m_time.wDayOfWeek -= 7; + } + m_time.wHour -= 24; + } + int leap = ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ); + while( m_time.wDay > m_monthdaycounts[ leap ][ m_time.wMonth ] ) { + + m_time.wDay -= m_monthdaycounts[ leap ][ m_time.wMonth ]; + ++m_time.wMonth; + // unlikely but we might've entered a new year + if( m_time.wMonth > 12 ) { + + ++m_time.wYear; + leap = ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ); + m_time.wMonth -= 12; + } + } +} + +int +scenario_time::year_day( int Day, const int Month, const int Year ) const { + + char const daytab[ 2 ][ 13 ] = { + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + + int leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; + for( int i = 1; i < Month; ++i ) + Day += daytab[ leap ][ i ]; + + return Day; +} + +void +scenario_time::daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday ) { + + WORD daytab[ 2 ][ 13 ] = { + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + + int leap = ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ); + WORD idx = 1; + while( ( idx < 13 ) && ( Yearday >= daytab[ leap ][ idx ] ) ) { + + ++idx; + } + Month = idx; + Day = Yearday - daytab[ leap ][ idx - 1 ]; +} + +int +scenario_time::julian_day() const { + + int yy = ( m_time.wYear < 0 ? m_time.wYear + 1 : m_time.wYear ) - std::floor( ( 12 - m_time.wMonth ) / 10.f ); + int mm = m_time.wMonth + 9; + if( mm >= 12 ) { mm -= 12; } + + int K1 = std::floor( 365.25 * ( yy + 4712 ) ); + int K2 = std::floor( 30.6 * mm + 0.5 ); + + // for dates in Julian calendar + int JD = K1 + K2 + m_time.wDay + 59; + // for dates in Gregorian calendar; 2299160 is October 15th, 1582 + const int gregorianswitchday = 2299160; + if( JD > gregorianswitchday ) { + + int K3 = std::floor( std::floor( ( yy * 0.01 ) + 49 ) * 0.75 ) - 38; + JD -= K3; + } + + return JD; +} + +//--------------------------------------------------------------------------- diff --git a/simulationtime.h b/simulationtime.h new file mode 100644 index 00000000..069a66ca --- /dev/null +++ b/simulationtime.h @@ -0,0 +1,66 @@ +/* +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/. +*/ + +#pragma once + +// wrapper for scenario time +class scenario_time { + +public: + scenario_time() { + m_time.wHour = 10; m_time.wMinute = 30; } + void + init(); + void + update( double const Deltatime ); + inline + SYSTEMTIME & + data() { + return m_time; } + inline + SYSTEMTIME const & + data() const { + return m_time; } + inline + double + second() const { + return ( m_time.wMilliseconds * 0.001 + m_time.wSecond ); } + inline + int + year_day() const { + return m_yearday; } + // helper, calculates day of year from given date + int + year_day( int Day, int const Month, int const Year ) const; + int + julian_day() const; + inline + double + zone_bias() const { + return m_timezonebias; } + +private: + // calculates day and month from given day of year + void + daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday ); + + SYSTEMTIME m_time; + double m_milliseconds{ 0.0 }; + int m_yearday; + char m_monthdaycounts[ 2 ][ 13 ]; + double m_timezonebias{ 0.0 }; +}; + +namespace simulation { + +extern scenario_time Time; + +} // simulation + +//--------------------------------------------------------------------------- diff --git a/sky.h b/sky.h index e583ff97..571b7aa6 100644 --- a/sky.h +++ b/sky.h @@ -14,7 +14,7 @@ http://mozilla.org/MPL/2.0/. class TSky { - friend class opengl_renderer; + friend opengl_renderer; public: TSky() = default; diff --git a/stars.h b/stars.h index 0de93ea6..a76973a9 100644 --- a/stars.h +++ b/stars.h @@ -8,7 +8,7 @@ class cStars { - friend class opengl_renderer; + friend opengl_renderer; public: // types: diff --git a/station.cpp b/station.cpp index ca440dcb..cf09b863 100644 --- a/station.cpp +++ b/station.cpp @@ -13,6 +13,12 @@ http://mozilla.org/MPL/2.0/. #include "dynobj.h" #include "mtable.h" +namespace simulation { + +basic_station Station; + +} + // exchanges load with consist attached to specified vehicle, operating on specified schedule double basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform ) { diff --git a/station.h b/station.h index 5c7c760b..fca9d922 100644 --- a/station.h +++ b/station.h @@ -19,4 +19,10 @@ public: // exchanges load with consist attached to specified vehicle, operating on specified schedule; returns: time needed for exchange, in seconds double update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform ); -}; \ No newline at end of file +}; + +namespace simulation { + +extern basic_station Station; // temporary object, for station functionality tests + +} // simulation diff --git a/sun.cpp b/sun.cpp index fc264f76..c29c5856 100644 --- a/sun.cpp +++ b/sun.cpp @@ -4,7 +4,7 @@ #include "globals.h" #include "mtable.h" #include "utilities.h" -#include "world.h" +#include "simulationtime.h" ////////////////////////////////////////////////////////////////////////////////////////// // cSun -- class responsible for dynamic calculation of position and intensity of the Sun, diff --git a/uilayer.cpp b/uilayer.cpp index 260710c0..041b0f84 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -6,6 +6,7 @@ #include "globals.h" #include "translation.h" #include "simulation.h" +#include "simulationtime.h" #include "mtable.h" #include "train.h" #include "sceneeditor.h" diff --git a/utilities.cpp b/utilities.cpp index bd8207b2..0a8e603b 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -257,18 +257,24 @@ int stol_def(const std::string &str, const int &DefaultValue) { return result; } -std::string ToLower(std::string const &text) -{ - std::string lowercase( text ); - std::transform(text.begin(), text.end(), lowercase.begin(), ::tolower); +std::string ToLower(std::string const &text) { + + auto lowercase { text }; + std::transform( + std::begin( text ), std::end( text ), + std::begin( lowercase ), + []( unsigned char c ) { return std::tolower( c ); } ); return lowercase; } -std::string ToUpper(std::string const &text) -{ - std::string uppercase( text ); - std::transform(text.begin(), text.end(), uppercase.begin(), ::toupper); - return uppercase; +std::string ToUpper(std::string const &text) { + + auto uppercase { text }; + std::transform( + std::begin( text ), std::end( text ), + std::begin( uppercase ), + []( unsigned char c ) { return std::toupper( c ); } ); + return uppercase; } // replaces polish letters with basic ascii diff --git a/windows.cpp b/windows.cpp index 5d9cef25..a25e6ae7 100644 --- a/windows.cpp +++ b/windows.cpp @@ -50,7 +50,6 @@ LONG CALLBACK unhandled_handler(::EXCEPTION_POINTERS* e) HWND Hwnd; WNDPROC BaseWindowProc; PCOPYDATASTRUCT pDane; -extern TWorld World; LRESULT APIENTRY WndProc( HWND hWnd, // handle for this window UINT uMsg, // message for this window