diff --git a/AnimModel.cpp b/AnimModel.cpp index 62d0b5e0..b4b40cf0 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "MdlMngr.h" #include "simulation.h" #include "simulationtime.h" +#include "event.h" #include "Globals.h" #include "Timer.h" #include "Logs.h" @@ -610,7 +611,7 @@ void TAnimModel::RaPrepare() if (LightsOff[i]) LightsOff[i]->iVisible = !state; } - TSubModel::iInstance = (size_t)this; //żeby nie robić cudzych animacji + TSubModel::iInstance = reinterpret_cast( this ); //żeby nie robić cudzych animacji TSubModel::pasText = &asText; // przekazanie tekstu do wyświetlacza (!!!! do przemyślenia) if (pAdvanced) // jeśli jest zaawansowana animacja Advanced(); // wykonać co tam trzeba diff --git a/AnimModel.h b/AnimModel.h index 626386ba..16f3a558 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -125,7 +125,7 @@ class TAnimAdvanced class TAnimModel : public scene::basic_node { friend opengl_renderer; - friend ui_layer; + friend editor_ui; public: // constructors diff --git a/Camera.cpp b/Camera.cpp index f1e077ca..47272eb3 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -18,16 +18,16 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- -void TCamera::Init( Math3D::vector3 NPos, Math3D::vector3 NAngle) { +void TCamera::Init( Math3D::vector3 const &NPos, Math3D::vector3 const &NAngle, TCameraType const NType ) { - vUp = Math3D::vector3(0, 1, 0); - Velocity = Math3D::vector3(0, 0, 0); + vUp = { 0, 1, 0 }; + Velocity = { 0, 0, 0 }; Pitch = NAngle.x; Yaw = NAngle.y; Roll = NAngle.z; Pos = NPos; - Type = (Global.bFreeFly ? TCameraType::tp_Free : TCameraType::tp_Follow); + Type = NType; }; void TCamera::Reset() { diff --git a/Camera.h b/Camera.h index 792f488d..b2792b18 100644 --- a/Camera.h +++ b/Camera.h @@ -23,7 +23,7 @@ enum class TCameraType class TCamera { public: // McZapkie: potrzebuje do kiwania na boki - void Init( Math3D::vector3 NPos, Math3D::vector3 NAngle); + void Init( Math3D::vector3 const &Location, Math3D::vector3 const &Angle, TCameraType const Type ); void Reset(); void OnCursorMove(double const x, double const y); bool OnCommand( command_data const &Command ); diff --git a/Classes.h b/Classes.h index e146f462..e1c3d2b5 100644 --- a/Classes.h +++ b/Classes.h @@ -28,11 +28,17 @@ class sound_source; class TEventLauncher; class TTraction; // drut class TTractionPowerSource; // zasilanie drutów -class TWorld; class TCamera; class scenario_time; class TMoverParameters; class ui_layer; +class editor_ui; +class event_manager; +class memory_table; +class powergridsource_table; +class instance_table; +class vehicle_table; +struct light_array; namespace scene { struct node_data; diff --git a/Console.cpp b/Console.cpp index 6de96f69..42e536f6 100644 --- a/Console.cpp +++ b/Console.cpp @@ -89,8 +89,7 @@ Console::Console() Console::~Console() { - delete PoKeys55[0]; - delete PoKeys55[1]; + Console::Off(); }; void Console::ModeSet(int m, int h) diff --git a/Driver.cpp b/Driver.cpp index 6d9c4430..2e9baffd 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -18,11 +18,12 @@ http://mozilla.org/MPL/2.0/. #include #include "Globals.h" #include "Logs.h" +#include "train.h" #include "mtable.h" #include "DynObj.h" #include "Event.h" #include "MemCell.h" -#include "world.h" +#include "simulation.h" #include "simulationtime.h" #include "track.h" #include "station.h" @@ -1725,7 +1726,11 @@ void TController::Activation() } if (pVehicle != old) { // jeśli zmieniony został pojazd prowadzony - Global.pWorld->CabChange(old, pVehicle); // ewentualna zmiana kabiny użytkownikowi + if( ( simulation::Train ) + && ( simulation::Train->Dynamic() == old ) ) { + // ewentualna zmiana kabiny użytkownikowi + Global.changeDynObj = pVehicle; // uruchomienie protezy + } ControllingSet(); // utworzenie połączenia do sterowanego pojazdu (może się zmienić) - // silnikowy dla EZT } diff --git a/DynObj.cpp b/DynObj.cpp index ee5920b6..f6b98ae2 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -16,15 +16,17 @@ http://mozilla.org/MPL/2.0/. #include "DynObj.h" #include "simulation.h" -#include "world.h" #include "train.h" +#include "driver.h" #include "Globals.h" #include "Timer.h" #include "logs.h" #include "Console.h" #include "MdlMngr.h" +#include "model3d.h" #include "renderer.h" #include "uitranscripts.h" +#include "messaging.h" // Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny #define vWorldFront Math3D::vector3(0, 0, 1) @@ -4305,7 +4307,7 @@ void TDynamicObject::RenderSounds() { FreeFlyModeFlag ? true : // in external view all vehicles emit outer noise // Global.pWorld->train() == nullptr ? true : // (can skip this check, with no player train the external view is a given) ctOwner == nullptr ? true : // standalone vehicle, can't be part of user-driven train - ctOwner != Global.pWorld->train()->Dynamic()->ctOwner ? true : // confirmed isn't a part of the user-driven train + ctOwner != simulation::Train->Dynamic()->ctOwner ? true : // confirmed isn't a part of the user-driven train Global.CabWindowOpen ? true : // sticking head out we get to hear outer noise false ) ) { @@ -6994,8 +6996,11 @@ vehicle_table::erase_disabled() { && ( true == vehicle->MyTrack->RemoveDynamicObject( vehicle ) ) ) { vehicle->MyTrack = nullptr; } - // clear potential train binding - Global.pWorld->TrainDelete( vehicle ); + if( simulation::Train->Dynamic() == vehicle ) { + // clear potential train binding + // TBD, TODO: manually eject the driver first ? + SafeDelete( simulation::Train ); + } // remove potential entries in the light array simulation::Lights.remove( vehicle ); /* diff --git a/Event.cpp b/Event.cpp index b639a72d..13646dd3 100644 --- a/Event.cpp +++ b/Event.cpp @@ -17,8 +17,16 @@ http://mozilla.org/MPL/2.0/. #include "event.h" #include "simulation.h" -#include "world.h" +#include "messaging.h" #include "globals.h" +#include "memcell.h" +#include "track.h" +#include "traction.h" +#include "tractionpower.h" +#include "sound.h" +#include "animmodel.h" +#include "dynobj.h" +#include "driver.h" #include "timer.h" #include "logs.h" @@ -1207,7 +1215,7 @@ event_manager::CheckQuery() { } case 1: { if( m_workevent->Params[ 1 ].asdouble > 0.0 ) { - Global.pWorld->radio_message( + simulation::radio_message( m_workevent->Params[ 9 ].tsTextSound, static_cast( m_workevent->Params[ 1 ].asdouble ) ); } diff --git a/Globals.cpp b/Globals.cpp index df17cb64..10b63e7b 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -14,8 +14,9 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "globals.h" -#include "world.h" #include "simulation.h" +#include "simulationenvironment.h" +#include "driver.h" #include "logs.h" #include "Console.h" #include "PyInt.h" @@ -94,7 +95,7 @@ global_settings::ConfigParse(cParser &Parser) { { // Mczapkie-130302 Parser.getTokens(); - Parser >> bFreeFly; + Parser >> FreeFlyModeFlag; Parser.getTokens(3, false); Parser >> FreeCameraInit[0].x, @@ -298,7 +299,7 @@ global_settings::ConfigParse(cParser &Parser) { std::tm *localtime = std::localtime(&timenow); fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku } - pWorld->compute_season( fMoveLight ); + simulation::Environment.compute_season( fMoveLight ); } else if( token == "dynamiclights" ) { // number of dynamic lights in the scene diff --git a/Globals.h b/Globals.h index b924df68..f542aac1 100644 --- a/Globals.h +++ b/Globals.h @@ -25,8 +25,8 @@ struct global_settings { bool altState{ false }; std::mt19937 random_engine{ std::mt19937( static_cast( std::time( NULL ) ) ) }; TDynamicObject *changeDynObj{ nullptr };// info o zmianie pojazdu - TWorld *pWorld{ nullptr }; // wskaźnik na świat do usuwania pojazdów TCamera *pCamera{ nullptr }; // parametry kamery + TCamera *pDebugCamera{ nullptr }; Math3D::vector3 pCameraPosition; // pozycja kamery w świecie Math3D::vector3 DebugCameraPosition; // pozycja kamery w świecie std::vector FreeCameraInit; // pozycje kamery @@ -64,7 +64,6 @@ struct global_settings { unsigned int DisabledLogTypes{ 0 }; // simulation bool RealisticControlMode{ false }; // controls ability to steer the vehicle from outside views - bool bFreeFly{ false }; bool bEnableTraction{ true }; float fFriction{ 1.f }; // mnożnik tarcia - KURS90 bool bLiveTraction{ true }; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 69801a14..17b27181 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1294,7 +1294,7 @@ public: void ComputeConstans(void);//ABu: wczesniejsze wyznaczenie stalych dla liczenia sil double ComputeMass(void); void ComputeTotalForce(double dt, double dt1, bool FullVer); - double Adhesive(double staticfriction); + double Adhesive(double staticfriction) const; double TractionForce(double dt); double FrictionForce(double R, int TDamage); double BrakeForceR(double ratio, double velocity); @@ -1327,8 +1327,8 @@ public: bool FuseOn(void); //bezpiecznik nadamiary bool FuseFlagCheck(void); // sprawdzanie flagi nadmiarowego void FuseOff(void); // wylaczenie nadmiarowego - double ShowCurrent( int AmpN ); //pokazuje bezwgl. wartosc pradu na wybranym amperomierzu - double ShowCurrentP(int AmpN); //pokazuje bezwgl. wartosc pradu w wybranym pojezdzie //Q 20160722 + double ShowCurrent( int AmpN ) const; //pokazuje bezwgl. wartosc pradu na wybranym amperomierzu + double ShowCurrentP(int AmpN) const; //pokazuje bezwgl. wartosc pradu w wybranym pojezdzie //Q 20160722 /*!o pokazuje bezwgl. wartosc obrotow na obrotomierzu jednego z 3 pojazdow*/ /*function ShowEngineRotation(VehN:int): integer; //Ra 2014-06: przeniesione do C++*/ @@ -1374,7 +1374,7 @@ public: /*funkcje ladujace pliki opisujace pojazd*/ bool LoadFIZ(std::string chkpath); //Q 20160717 bool LoadChkFile(std::string chkpath); bool CheckLocomotiveParameters( bool ReadyFlag, int Dir ); - std::string EngineDescription( int what ); + std::string EngineDescription( int what ) const; private: void LoadFIZ_Param( std::string const &line ); void LoadFIZ_Load( std::string const &line ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 8b1b5c17..7289e340 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1629,7 +1629,7 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) { } -double TMoverParameters::ShowCurrent(int AmpN) +double TMoverParameters::ShowCurrent(int AmpN) const { // Odczyt poboru prądu na podanym amperomierzu switch (EngineType) { @@ -4124,7 +4124,7 @@ double TMoverParameters::FrictionForce(double R, int TDamage) // Q: 20160713 // Oblicza przyczepność // ************************************************************************************************* -double TMoverParameters::Adhesive(double staticfriction) +double TMoverParameters::Adhesive(double staticfriction) const { double adhesion = 0.0; const double adh_factor = 0.25; //współczynnik określający, jak bardzo spada tarcie przy poślizgu @@ -6763,7 +6763,7 @@ bool TMoverParameters::ChangeOffsetH(double DeltaOffset) // Q: 20160713 // Testuje zmienną (narazie tylko 0) i na podstawie uszkodzenia zwraca informację tekstową // ************************************************************************************************* -std::string TMoverParameters::EngineDescription(int what) +std::string TMoverParameters::EngineDescription(int what) const { std::string outstr { "OK" }; switch (what) { @@ -9599,7 +9599,7 @@ bool TMoverParameters::RunInternalCommand() // Q: 20160714 // Zwraca wartość natężenia prądu na wybranym amperomierzu. Podfunkcja do ShowCurrent. // ************************************************************************************************* -double TMoverParameters::ShowCurrentP(int AmpN) +double TMoverParameters::ShowCurrentP(int AmpN) const { int b, Bn; bool Grupowy; diff --git a/MemCell.cpp b/MemCell.cpp index 0fab54cc..b958d37e 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -17,6 +17,8 @@ http://mozilla.org/MPL/2.0/. #include "memcell.h" #include "simulation.h" +#include "driver.h" +#include "event.h" #include "logs.h" //--------------------------------------------------------------------------- diff --git a/Model3d.cpp b/Model3d.cpp index 26b3e3b3..60243a7d 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 "simulation.h" #include "simulationtime.h" #include "mtable.h" #include "sn_utils.h" @@ -28,7 +29,7 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others using namespace Mtable; float TSubModel::fSquareDist = 0.f; -size_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu +std::uintptr_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu texture_handle const *TSubModel::ReplacableSkinId = NULL; int TSubModel::iAlpha = 0x30300030; // maska do testowania flag tekstur wymiennych TModel3d *TSubModel::pRoot; // Ra: tymczasowo wskaźnik na model widoczny z submodelu @@ -1191,7 +1192,7 @@ TSubModel::offset( float const Geometrytestoffsetthreshold ) const { if( true == TestFlag( iFlags, 0x0200 ) ) { // flip coordinates for t3d file which wasn't yet initialized - if( ( false == Global.pWorld->InitPerformed() ) + if( ( false == simulation::is_ready ) || ( false == Vertices.empty() ) ) { // NOTE, HACK: results require flipping if the model wasn't yet initialized, so we're using crude method to detect possible cases // TODO: sort out this mess, either unify offset lookups to take place before (or after) initialization, diff --git a/Model3d.h b/Model3d.h index d2c8d17b..6ae4b2f3 100644 --- a/Model3d.h +++ b/Model3d.h @@ -135,7 +135,7 @@ public: // chwilowo float3 v_TransVector { 0.0f, 0.0f, 0.0f }; gfx::vertex_array Vertices; float m_boundingradius { 0 }; - size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację + std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) diff --git a/Segment.cpp b/Segment.cpp index 5d117c14..5f2e16b4 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -203,16 +203,15 @@ double TSegment::GetTFromS(double const s) const // initial guess for Newton's method double fTolerance = 0.001; double fRatio = s / RombergIntegral(0, 1); - double fOmRatio = 1.0 - fRatio; - double fTime = fOmRatio * 0 + fRatio * 1; - int iteration = 0; + double fTime = interpolate( 0.0, 1.0, fRatio ); + int iteration = 0; + double fDifference {}; // exposed for debug down the road do { - double fDifference = RombergIntegral(0, fTime) - s; + fDifference = RombergIntegral(0, fTime) - s; if( std::abs( fDifference ) < fTolerance ) { return fTime; } - fTime -= fDifference / GetFirstDerivative(fTime).Length(); ++iteration; } @@ -323,7 +322,7 @@ Math3D::vector3 TSegment::FastGetDirection(double fDistance, double fOffset) return (Point2 - CPointIn); // wektor na końcu jest stały return (FastGetPoint(t2) - FastGetPoint(t1)); } - +/* Math3D::vector3 TSegment::GetPoint(double const fDistance) const { // wyliczenie współrzędnych XYZ na torze w odległości (fDistance) od Point1 if (bCurve) @@ -332,13 +331,17 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const // return Interpolate(t,Point1,CPointOut,CPointIn,Point2); return RaInterpolate(t); } - else - { // wyliczenie dla odcinka prostego jest prostsze - double t = fDistance / fLength; // zerowych torów nie ma - return ((1.0 - t) * Point1 + (t)*Point2); + else { + // wyliczenie dla odcinka prostego jest prostsze + return + interpolate( + Point1, Point2, + clamp( + fDistance / fLength, + 0.0, 1.0 ) ); } }; - +*/ // ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const { diff --git a/Segment.h b/Segment.h index 9410a2f7..81970cd9 100644 --- a/Segment.h +++ b/Segment.h @@ -88,8 +88,10 @@ public: return CPointOut; }; Math3D::vector3 FastGetDirection(double const fDistance, double const fOffset); +/* Math3D::vector3 GetPoint(double const fDistance) const; +*/ void RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const; Math3D::vector3 diff --git a/Track.cpp b/Track.cpp index 375b270c..31787522 100644 --- a/Track.cpp +++ b/Track.cpp @@ -17,6 +17,11 @@ http://mozilla.org/MPL/2.0/. #include "simulation.h" #include "globals.h" +#include "event.h" +#include "messaging.h" +#include "dynobj.h" +#include "animmodel.h" +#include "track.h" #include "timer.h" #include "logs.h" #include "renderer.h" diff --git a/Track.h b/Track.h index 160fad5a..4218ef0e 100644 --- a/Track.h +++ b/Track.h @@ -128,7 +128,7 @@ class TTrack : public scene::basic_node { friend opengl_renderer; // NOTE: temporary arrangement - friend ui_layer; + friend editor_ui; private: TIsolated * pIsolated = nullptr; // obwód izolowany obsługujący zajęcia/zwolnienia grupy torów diff --git a/Traction.cpp b/Traction.cpp index 5356c4d5..71ec2aee 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "simulation.h" #include "Globals.h" +#include "tractionpower.h" #include "logs.h" #include "renderer.h" diff --git a/Train.cpp b/Train.cpp index 88b1bbb4..69ffef85 100644 --- a/Train.cpp +++ b/Train.cpp @@ -21,8 +21,10 @@ http://mozilla.org/MPL/2.0/. #include "camera.h" #include "Logs.h" #include "MdlMngr.h" +#include "model3d.h" #include "Timer.h" #include "Driver.h" +#include "dynobj.h" #include "mtable.h" #include "Console.h" diff --git a/TrkFoll.cpp b/TrkFoll.cpp index 37029750..b1bfab6c 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -18,6 +18,8 @@ http://mozilla.org/MPL/2.0/. #include "simulation.h" #include "Globals.h" +#include "dynobj.h" +#include "driver.h" #include "Logs.h" TTrackFollower::~TTrackFollower() diff --git a/application.cpp b/application.cpp index 4bbca8a0..6c457219 100644 --- a/application.cpp +++ b/application.cpp @@ -9,14 +9,13 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "application.h" +#include "scenarioloadermode.h" +#include "drivermode.h" +#include "editormode.h" #include "globals.h" -#include "keyboardinput.h" -#include "mouseinput.h" -#include "gamepadinput.h" -#include "console.h" #include "simulation.h" -#include "world.h" +#include "train.h" #include "pyint.h" #include "sceneeditor.h" #include "renderer.h" @@ -45,17 +44,7 @@ http://mozilla.org/MPL/2.0/. 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 - -} +ui_layer uilayerstaticinitializer; #ifdef _WIN32 extern "C" @@ -71,6 +60,14 @@ extern WNDPROC BaseWindowProc; // user input callbacks +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 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? @@ -81,101 +78,23 @@ void window_resize_callback( GLFWwindow *window, int w, int 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 ); + Application.on_cursor_pos( 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 + Application.on_mouse_button( button, action, mods ); } 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 ); - } + Application.on_scroll( xoffset, yoffset ); +} + +void key_callback( GLFWwindow *window, int key, int scancode, int action, int mods ) { + + Application.on_key( key, scancode, action, mods ); } // public: @@ -190,6 +109,12 @@ eu07_application::init( int Argc, char *Argv[] ) { if( ( result = init_settings( Argc, Argv ) ) != 0 ) { return result; } + + WriteLog( "Starting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")" ); + WriteLog( "For online documentation and additional files refer to: http://eu07.pl" ); + WriteLog( "Authors: Marcin_EU, McZapkie, ABu, Winger, Tolaris, nbmx, OLO_EU, Bart, Quark-t, " + "ShaXbee, Oli_EU, youBy, KURS90, Ra, hunter, szociu, Stele, Q, firleju and others\n" ); + if( ( result = init_glfw() ) != 0 ) { return result; } @@ -200,6 +125,9 @@ eu07_application::init( int Argc, char *Argv[] ) { if( ( result = init_audio() ) != 0 ) { return result; } + if( ( result = init_modes() ) != 0 ) { + return result; + } return result; } @@ -207,56 +135,12 @@ eu07_application::init( int Argc, char *Argv[] ) { 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() ) + && ( false == m_modestack.empty() ) + && ( true == m_modes[ m_modestack.top() ]->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; @@ -265,9 +149,7 @@ eu07_application::run() { void eu07_application::exit() { -#ifdef _WIN32 - Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej) -#endif + SafeDelete( simulation::Train ); SafeDelete( simulation::Region ); glfwDestroyWindow( m_window ); @@ -276,28 +158,103 @@ eu07_application::exit() { TPythonInterpreter::killInstance(); } +void +eu07_application::render_ui() { + + if( m_modestack.empty() ) { return; } + + m_modes[ m_modestack.top() ]->render_ui(); +} + +bool +eu07_application::pop_mode() { + + if( m_modestack.empty() ) { return false; } + + m_modes[ m_modestack.top() ]->exit(); + m_modestack.pop(); + return true; +} + +bool +eu07_application::push_mode( eu07_application::mode const Mode ) { + + if( Mode >= mode::count_ ) { return false; } + + m_modes[ Mode ]->enter(); + m_modestack.push( Mode ); + + return true; +} + +void +eu07_application::set_title( std::string const &Title ) { + + glfwSetWindowTitle( m_window, Title.c_str() ); +} + +void +eu07_application::set_progress( float const Progress, float const Subtaskprogress ) { + + if( m_modestack.empty() ) { return; } + + m_modes[ m_modestack.top() ]->set_progress( Progress, Subtaskprogress ); +} + void eu07_application::set_cursor( int const Mode ) { - UILayer.set_cursor( Mode ); + ui_layer::set_cursor( Mode ); } void -eu07_application::set_cursor_pos( double const X, double const Y ) { +eu07_application::set_cursor_pos( double const Horizontal, double const Vertical ) { if( m_window != nullptr ) { - glfwSetCursorPos( m_window, X, Y ); + glfwSetCursorPos( m_window, Horizontal, Vertical ); } } void -eu07_application::get_cursor_pos( double &X, double &Y ) const { +eu07_application::get_cursor_pos( double &Horizontal, double &Vertical ) const { if( m_window != nullptr ) { - glfwGetCursorPos( m_window, &X, &Y ); + glfwGetCursorPos( m_window, &Horizontal, &Vertical ); } } +void +eu07_application::on_key( int const Key, int const Scancode, int const Action, int const Mods ) { + + if( m_modestack.empty() ) { return; } + + m_modes[ m_modestack.top() ]->on_key( Key, Scancode, Action, Mods ); +} + +void +eu07_application::on_cursor_pos( double const Horizontal, double const Vertical ) { + + if( m_modestack.empty() ) { return; } + + m_modes[ m_modestack.top() ]->on_cursor_pos( Horizontal, Vertical ); +} + +void +eu07_application::on_mouse_button( int const Button, int const Action, int const Mods ) { + + if( m_modestack.empty() ) { return; } + + m_modes[ m_modestack.top() ]->on_mouse_button( Button, Action, Mods ); +} + +void +eu07_application::on_scroll( double const Xoffset, double const Yoffset ) { + + if( m_modestack.empty() ) { return; } + + m_modes[ m_modestack.top() ]->on_scroll( Xoffset, Yoffset ); +} + // private: void @@ -385,13 +342,7 @@ eu07_application::init_settings( int Argc, char *Argv[] ) { 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( token == "-s" ) { if( i + 1 < Argc ) { Global.SceneryFile = ToLower( Argv[ ++i ] ); } @@ -406,7 +357,6 @@ eu07_application::init_settings( int Argc, char *Argv[] ) { << "usage: " << std::string( Argv[ 0 ] ) << " [-s sceneryfilepath]" << " [-v vehiclename]" - << " [-e3d]" << std::endl; return -1; } @@ -468,7 +418,6 @@ eu07_application::init_glfw() { // 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; @@ -499,7 +448,7 @@ eu07_application::init_gfx() { } if( ( false == GfxRenderer.Init( m_window ) ) - || ( false == UILayer.init( m_window ) ) ) { + || ( false == ui_layer::init( m_window ) ) ) { return -1; } @@ -515,3 +464,25 @@ eu07_application::init_audio() { // NOTE: lack of audio isn't deemed a failure serious enough to throw in the towel return 0; } + +int +eu07_application::init_modes() { + + // NOTE: we could delay creation/initialization until transition to specific mode is requested, + // but doing it in one go at the start saves us some error checking headache down the road + + // create all application behaviour modes + m_modes[ mode::scenarioloader ] = std::make_shared(); + m_modes[ mode::driver ] = std::make_shared(); + m_modes[ mode::editor ] = std::make_shared(); + // initialize the mode objects + for( auto &mode : m_modes ) { + if( false == mode->init() ) { + return -1; + } + } + // activate the default mode + push_mode( mode::scenarioloader ); + + return 0; +} diff --git a/application.h b/application.h index 719f8ade..d7cbea69 100644 --- a/application.h +++ b/application.h @@ -7,9 +7,21 @@ obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include "applicationmode.h" + class eu07_application { public: +// types + enum mode { +// launcher = 0, + scenarioloader, + driver, + editor, + count_ + }; // constructors eu07_application() = default; // methods @@ -19,18 +31,41 @@ public: run(); void exit(); + void + render_ui(); + // switches application to specified mode + bool + pop_mode(); + bool + push_mode( eu07_application::mode const Mode ); + void + set_title( std::string const &Title ); + void + set_progress( float const Progress = 0.f, float const Subtaskprogress = 0.f ); void set_cursor( int const Mode ); void - set_cursor_pos( double const X, double const Y ); + set_cursor_pos( double const Horizontal, double const Vertical ); void - get_cursor_pos( double &X, double &Y ) const; + get_cursor_pos( double &Horizontal, double &Vertical ) const; + // input handlers + void + on_key( int const Key, int const Scancode, int const Action, int const Mods ); + void + on_cursor_pos( double const Horizontal, double const Vertical ); + void + on_mouse_button( int const Button, int const Action, int const Mods ); + void + on_scroll( double const Xoffset, double const Yoffset ); inline GLFWwindow * window() { return m_window; } private: +// types + using modeptr_array = std::array, static_cast( mode::count_ )>; + using mode_stack = std::stack; // methods void init_debug(); void init_files(); @@ -39,8 +74,11 @@ private: void init_callbacks(); int init_gfx(); int init_audio(); + int init_modes(); // members GLFWwindow * m_window { nullptr }; + modeptr_array m_modes { nullptr }; // collection of available application behaviour modes + mode_stack m_modestack; // current behaviour mode }; -extern eu07_application Application; \ No newline at end of file +extern eu07_application Application; diff --git a/applicationmode.h b/applicationmode.h new file mode 100644 index 00000000..3bba4c28 --- /dev/null +++ b/applicationmode.h @@ -0,0 +1,67 @@ +/* +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 + +#include "uilayer.h" + +// component implementing specific mode of application behaviour +// base interface +class application_mode { + +public: +// destructor + virtual + ~application_mode() = default; +// methods; + // initializes internal data structures of the mode. returns: true on success, false otherwise + virtual + bool + init() = 0; + // mode-specific update of simulation data. returns: false on error, true otherwise + virtual + bool + update() = 0; + // draws node-specific user interface + inline + void + render_ui() { + if( m_userinterface != nullptr ) { + m_userinterface->render(); } } + inline + void + set_progress( float const Progress = 0.f, float const Subtaskprogress = 0.f ) { + if( m_userinterface != nullptr ) { + m_userinterface->set_progress( Progress, Subtaskprogress ); } } + // maintenance method, called when the mode is activated + virtual + void + enter() = 0; + // maintenance method, called when the mode is deactivated + virtual + void + exit() = 0; + // input handlers + virtual + void + on_key( int const Key, int const Scancode, int const Action, int const Mods ) = 0; + virtual + void + on_cursor_pos( double const X, double const Y ) = 0; + virtual + void + on_mouse_button( int const Button, int const Action, int const Mods ) = 0; + virtual + void + on_scroll( double const Xoffset, double const Yoffset ) = 0; + +protected: +// members + std::shared_ptr m_userinterface; +}; diff --git a/combustionengine.cpp b/combustionengine.cpp new file mode 100644 index 00000000..d751f06d --- /dev/null +++ b/combustionengine.cpp @@ -0,0 +1,13 @@ +/* +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 "combustionengine.h" + +//--------------------------------------------------------------------------- diff --git a/combustionengine.h b/combustionengine.h new file mode 100644 index 00000000..07a378bd --- /dev/null +++ b/combustionengine.h @@ -0,0 +1,20 @@ +/* +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 + +// basic approximation of a fuel pump +// TODO: fuel consumption, optional automatic engine start after activation +struct fuel_pump { + + bool is_enabled { false }; // device is allowed/requested to operate + bool is_active { false }; // device is working +}; + +//--------------------------------------------------------------------------- diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp new file mode 100644 index 00000000..c6f34552 --- /dev/null +++ b/driverkeyboardinput.cpp @@ -0,0 +1,215 @@ +/* +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 "driverkeyboardinput.h" + +bool +driverkeyboard_input::init() { + + default_bindings(); + + return recall_bindings(); +} + +void +driverkeyboard_input::default_bindings() { + + m_bindingsetups = { + { user_command::aidriverenable, GLFW_KEY_Q | keymodifier::shift }, + { user_command::aidriverdisable, GLFW_KEY_Q }, + { user_command::mastercontrollerincrease, GLFW_KEY_KP_ADD }, + { user_command::mastercontrollerincreasefast, GLFW_KEY_KP_ADD | keymodifier::shift }, + { user_command::mastercontrollerdecrease, GLFW_KEY_KP_SUBTRACT }, + { user_command::mastercontrollerdecreasefast, GLFW_KEY_KP_SUBTRACT | keymodifier::shift }, + // mastercontrollerset, + { user_command::secondcontrollerincrease, GLFW_KEY_KP_DIVIDE }, + { user_command::secondcontrollerincreasefast, GLFW_KEY_KP_DIVIDE | keymodifier::shift }, + { user_command::secondcontrollerdecrease, GLFW_KEY_KP_MULTIPLY }, + { user_command::secondcontrollerdecreasefast, GLFW_KEY_KP_MULTIPLY | keymodifier::shift }, + // secondcontrollerset, + { user_command::mucurrentindicatorothersourceactivate, GLFW_KEY_Z | keymodifier::shift }, + { user_command::independentbrakeincrease, GLFW_KEY_KP_1 }, + { user_command::independentbrakeincreasefast, GLFW_KEY_KP_1 | keymodifier::shift }, + { user_command::independentbrakedecrease, GLFW_KEY_KP_7 }, + { user_command::independentbrakedecreasefast, GLFW_KEY_KP_7 | keymodifier::shift }, + // independentbrakeset, + { user_command::independentbrakebailoff, GLFW_KEY_KP_4 }, + { user_command::trainbrakeincrease, GLFW_KEY_KP_3 }, + { user_command::trainbrakedecrease, GLFW_KEY_KP_9 }, + // trainbrakeset, + { user_command::trainbrakecharging, GLFW_KEY_KP_DECIMAL }, + { user_command::trainbrakerelease, GLFW_KEY_KP_6 }, + { user_command::trainbrakefirstservice, GLFW_KEY_KP_8 }, + { user_command::trainbrakeservice, GLFW_KEY_KP_5 }, + { user_command::trainbrakefullservice, GLFW_KEY_KP_2 }, + { user_command::trainbrakehandleoff, GLFW_KEY_KP_5 | keymodifier::control }, + { user_command::trainbrakeemergency, GLFW_KEY_KP_0 }, + { user_command::trainbrakebasepressureincrease, GLFW_KEY_KP_3 | keymodifier::control }, + { user_command::trainbrakebasepressuredecrease, GLFW_KEY_KP_9 | keymodifier::control }, + { user_command::trainbrakebasepressurereset, GLFW_KEY_KP_6 | keymodifier::control }, + { user_command::trainbrakeoperationtoggle, GLFW_KEY_KP_4 | keymodifier::control }, + { user_command::manualbrakeincrease, GLFW_KEY_KP_1 | keymodifier::control }, + { user_command::manualbrakedecrease, GLFW_KEY_KP_7 | keymodifier::control }, + { user_command::alarmchaintoggle, GLFW_KEY_B | keymodifier::shift | keymodifier::control }, + { user_command::wheelspinbrakeactivate, GLFW_KEY_KP_ENTER }, + { user_command::sandboxactivate, GLFW_KEY_S }, + { user_command::reverserincrease, GLFW_KEY_D }, + { user_command::reverserdecrease, GLFW_KEY_R }, + // reverserforwardhigh, + // reverserforward, + // reverserneutral, + // reverserbackward, + { user_command::waterpumpbreakertoggle, GLFW_KEY_W | keymodifier::control }, + // waterpumpbreakerclose, + // waterpumpbreakeropen, + { user_command::waterpumptoggle, GLFW_KEY_W }, + // waterpumpenable, + // waterpumpdisable, + { user_command::waterheaterbreakertoggle, GLFW_KEY_W | keymodifier::control | keymodifier::shift }, + // waterheaterbreakerclose, + // waterheaterbreakeropen, + { user_command::waterheatertoggle, GLFW_KEY_W | keymodifier::shift }, + // waterheaterenable, + // waterheaterdisable, + { user_command::watercircuitslinktoggle, GLFW_KEY_H | keymodifier::shift }, + // watercircuitslinkenable, + // watercircuitslinkdisable, + { user_command::fuelpumptoggle, GLFW_KEY_F }, + // fuelpumpenable, + // fuelpumpdisable, + { user_command::oilpumptoggle, GLFW_KEY_F | keymodifier::shift }, + // oilpumpenable, + // oilpumpdisable, + { user_command::linebreakertoggle, GLFW_KEY_M }, + // linebreakeropen, + // linebreakerclose, + { user_command::convertertoggle, GLFW_KEY_X }, + // converterenable, + // converterdisable, + { user_command::convertertogglelocal, GLFW_KEY_X | keymodifier::shift }, + { user_command::converteroverloadrelayreset, GLFW_KEY_N | keymodifier::control }, + { user_command::compressortoggle, GLFW_KEY_C }, + // compressorenable, + // compressordisable, + { user_command::compressortogglelocal, GLFW_KEY_C | keymodifier::shift }, + { user_command::motoroverloadrelaythresholdtoggle, GLFW_KEY_F }, + // motoroverloadrelaythresholdsetlow, + // motoroverloadrelaythresholdsethigh, + { user_command::motoroverloadrelayreset, GLFW_KEY_N }, + { user_command::notchingrelaytoggle, GLFW_KEY_G }, + { user_command::epbrakecontroltoggle, GLFW_KEY_Z | keymodifier::control }, + { user_command::trainbrakeoperationmodeincrease, GLFW_KEY_KP_2 | keymodifier::control }, + { user_command::trainbrakeoperationmodedecrease, GLFW_KEY_KP_8 | keymodifier::control }, + { user_command::brakeactingspeedincrease, GLFW_KEY_B | keymodifier::shift }, + { user_command::brakeactingspeeddecrease, GLFW_KEY_B }, + // brakeactingspeedsetcargo, + // brakeactingspeedsetpassenger, + // brakeactingspeedsetrapid, + { user_command::brakeloadcompensationincrease, GLFW_KEY_H | keymodifier::shift | keymodifier::control }, + { user_command::brakeloadcompensationdecrease, GLFW_KEY_H | keymodifier::control }, + { user_command::mubrakingindicatortoggle, GLFW_KEY_L | keymodifier::shift }, + { user_command::alerteracknowledge, GLFW_KEY_SPACE }, + { user_command::hornlowactivate, GLFW_KEY_A }, + { user_command::hornhighactivate, GLFW_KEY_S }, + { user_command::whistleactivate, GLFW_KEY_Z }, + { user_command::radiotoggle, GLFW_KEY_R | keymodifier::control }, + { user_command::radiochannelincrease, GLFW_KEY_R | keymodifier::shift }, + { user_command::radiochanneldecrease, GLFW_KEY_R }, + { user_command::radiostopsend, GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control }, + { user_command::radiostoptest, GLFW_KEY_R | keymodifier::shift | keymodifier::control }, + { user_command::cabchangeforward, GLFW_KEY_HOME }, + { user_command::cabchangebackward, GLFW_KEY_END }, + // viewturn, + // movehorizontal, + // movehorizontalfast, + // movevertical, + // moveverticalfast, + { user_command::moveleft, GLFW_KEY_LEFT }, + { user_command::moveright, GLFW_KEY_RIGHT }, + { user_command::moveforward, GLFW_KEY_UP }, + { user_command::moveback, GLFW_KEY_DOWN }, + { user_command::moveup, GLFW_KEY_PAGE_UP }, + { user_command::movedown, GLFW_KEY_PAGE_DOWN }, + { user_command::carcouplingincrease, GLFW_KEY_INSERT }, + { user_command::carcouplingdisconnect, GLFW_KEY_DELETE }, + { user_command::doortoggleleft, GLFW_KEY_COMMA }, + { user_command::doortoggleright, GLFW_KEY_PERIOD }, + { user_command::departureannounce, GLFW_KEY_SLASH }, + { user_command::doorlocktoggle, GLFW_KEY_S | keymodifier::shift }, + { user_command::pantographcompressorvalvetoggle, GLFW_KEY_V | keymodifier::control }, + { user_command::pantographcompressoractivate, GLFW_KEY_V | keymodifier::shift }, + { user_command::pantographtogglefront, GLFW_KEY_P }, + { user_command::pantographtogglerear, GLFW_KEY_O }, + // pantographraisefront, + // pantographraiserear, + // pantographlowerfront, + // pantographlowerrear, + { user_command::pantographlowerall, GLFW_KEY_P | keymodifier::control }, + { user_command::heatingtoggle, GLFW_KEY_H }, + // heatingenable, + // heatingdisable, + { user_command::lightspresetactivatenext, GLFW_KEY_T | keymodifier::shift }, + { user_command::lightspresetactivateprevious, GLFW_KEY_T }, + { user_command::headlighttoggleleft, GLFW_KEY_Y }, + // headlightenableleft, + // headlightdisableleft, + { user_command::headlighttoggleright, GLFW_KEY_I }, + // headlightenableright, + // headlightdisableright, + { user_command::headlighttoggleupper, GLFW_KEY_U }, + // headlightenableupper, + // headlightdisableupper, + { user_command::redmarkertoggleleft, GLFW_KEY_Y | keymodifier::shift }, + // redmarkerenableleft, + // redmarkerdisableleft, + { user_command::redmarkertoggleright, GLFW_KEY_I | keymodifier::shift }, + // redmarkerenableright, + // redmarkerdisableright, + { user_command::headlighttogglerearleft, GLFW_KEY_Y | keymodifier::control }, + { user_command::headlighttogglerearright, GLFW_KEY_I | keymodifier::control }, + { user_command::headlighttogglerearupper, GLFW_KEY_U | keymodifier::control }, + { user_command::redmarkertogglerearleft, GLFW_KEY_Y | keymodifier::control | keymodifier::shift }, + { user_command::redmarkertogglerearright, GLFW_KEY_I | keymodifier::control | keymodifier::shift }, + { user_command::redmarkerstoggle, GLFW_KEY_E | keymodifier::shift }, + { user_command::endsignalstoggle, GLFW_KEY_E }, + { user_command::headlightsdimtoggle, GLFW_KEY_L | keymodifier::control }, + // headlightsdimenable, + // headlightsdimdisable, + { user_command::motorconnectorsopen, GLFW_KEY_L }, + // motorconnectorsclose, + { user_command::motordisconnect, GLFW_KEY_E | keymodifier::control }, + { user_command::interiorlighttoggle, GLFW_KEY_APOSTROPHE }, + // interiorlightenable, + // interiorlightdisable, + { user_command::interiorlightdimtoggle, GLFW_KEY_APOSTROPHE | keymodifier::control }, + // interiorlightdimenable, + // interiorlightdimdisable, + { user_command::instrumentlighttoggle, GLFW_KEY_SEMICOLON }, + // instrumentlightenable, + // instrumentlightdisable, + { user_command::generictoggle0, GLFW_KEY_0 }, + { user_command::generictoggle1, GLFW_KEY_1 }, + { user_command::generictoggle2, GLFW_KEY_2 }, + { user_command::generictoggle3, GLFW_KEY_3 }, + { user_command::generictoggle4, GLFW_KEY_4 }, + { user_command::generictoggle5, GLFW_KEY_5 }, + { user_command::generictoggle6, GLFW_KEY_6 }, + { user_command::generictoggle7, GLFW_KEY_7 }, + { user_command::generictoggle8, GLFW_KEY_8 }, + { user_command::generictoggle9, GLFW_KEY_9 }, + { user_command::batterytoggle, GLFW_KEY_J }, + // batteryenable, + // batterydisable, + }; + + bind(); +} + +//--------------------------------------------------------------------------- diff --git a/driverkeyboardinput.h b/driverkeyboardinput.h new file mode 100644 index 00000000..c18871df --- /dev/null +++ b/driverkeyboardinput.h @@ -0,0 +1,27 @@ +/* +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 + +#include "keyboardinput.h" + +class driverkeyboard_input : public keyboard_input { + +public: +// methods + bool + init() override; + +protected: +// methods + void + default_bindings(); +}; + +//--------------------------------------------------------------------------- diff --git a/drivermode.cpp b/drivermode.cpp new file mode 100644 index 00000000..72283022 --- /dev/null +++ b/drivermode.cpp @@ -0,0 +1,1007 @@ +/* +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 "drivermode.h" +#include "driveruilayer.h" + +#include "globals.h" +#include "application.h" +#include "simulation.h" +#include "simulationtime.h" +#include "simulationenvironment.h" +#include "lightarray.h" +#include "train.h" +#include "driver.h" +#include "dynobj.h" +#include "model3d.h" +#include "event.h" +#include "messaging.h" +#include "timer.h" +#include "renderer.h" +#include "utilities.h" +#include "logs.h" + +namespace input { + +user_command command; // currently issued control command, if any + +} + +void +driver_mode::drivermode_input::poll() { + + keyboard.poll(); + if( true == Global.InputMouse ) { + mouse.poll(); + } + if( true == Global.InputGamepad ) { + gamepad.poll(); + } + if( uart != nullptr ) { + uart->poll(); + } + // TBD, TODO: wrap current command in object, include other input sources? + input::command = ( + mouse.command() != user_command::none ? + mouse.command() : + keyboard.command() ); +} + +bool +driver_mode::drivermode_input::init() { + + // initialize input devices + auto result = ( + keyboard.init() + && mouse.init() + && gamepad.init() ); + if( true == Global.uart_conf.enable ) { + uart = std::make_unique(); + result = ( result && uart->init() ); + } +#ifdef _WIN32 + Console::On(); // włączenie konsoli +#endif + + return result; +} + +driver_mode::driver_mode() { + + m_userinterface = std::make_shared(); +} + +// initializes internal data structures of the mode. returns: true on success, false otherwise +bool +driver_mode::init() { + + return m_input.init(); +} + +// mode-specific update of simulation data. returns: false on error, true otherwise +bool +driver_mode::update() { + + Timer::UpdateTimers(Global.iPause != 0); + Timer::subsystem.sim_total.start(); + + double const deltatime = Timer::GetDeltaTime(); // 0.0 gdy pauza + + if( Global.iPause == 0 ) { + // jak pauza, to nie ma po co tego przeliczać + simulation::Time.update( deltatime ); + } + simulation::State.update_clocks(); + simulation::Environment.update(); + + // fixed step, simulation time based updates +// m_primaryupdateaccumulator += dt; // unused for the time being + m_secondaryupdateaccumulator += deltatime; +/* + // NOTE: until we have no physics state interpolation during render, we need to rely on the old code, + // as doing fixed step calculations but flexible step render results in ugly mini jitter + // core routines (physics) + int updatecount = 0; + while( ( m_primaryupdateaccumulator >= m_primaryupdaterate ) + &&( updatecount < 20 ) ) { + // no more than 20 updates per single pass, to keep physics from hogging up all run time + Ground.Update( m_primaryupdaterate, 1 ); + ++updatecount; + m_primaryupdateaccumulator -= m_primaryupdaterate; + } +*/ + int updatecount = 1; + if( deltatime > m_primaryupdaterate ) // normalnie 0.01s + { +/* + // NOTE: experimentally disabled physics update cap + auto const iterations = std::ceil(dt / m_primaryupdaterate); + updatecount = std::min( 20, static_cast( iterations ) ); +*/ + updatecount = std::ceil( deltatime / m_primaryupdaterate ); +/* + // NOTE: changing dt wrecks things further down the code. re-acquire proper value later or cleanup here + dt = dt / iterations; // Ra: fizykę lepiej by było przeliczać ze stałym krokiem +*/ + } + auto const stepdeltatime { deltatime / updatecount }; + // NOTE: updates are limited to 20, but dt is distributed over potentially many more iterations + // this means at count > 20 simulation and render are going to desync. is that right? + // NOTE: experimentally changing this to prevent the desync. + // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps + Timer::subsystem.sim_dynamics.start(); + if( true == Global.FullPhysics ) { + // mixed calculation mode, steps calculated in ~0.05s chunks + while( updatecount >= 5 ) { + simulation::State.update( stepdeltatime, 5 ); + updatecount -= 5; + } + if( updatecount ) { + simulation::State.update( stepdeltatime, updatecount ); + } + } + else { + // simplified calculation mode; faster but can lead to errors + simulation::State.update( stepdeltatime, updatecount ); + } + Timer::subsystem.sim_dynamics.stop(); + + // secondary fixed step simulation time routines + while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) { + + // awaria PoKeys mogła włączyć pauzę - przekazać informację + if( Global.iMultiplayer ) // dajemy znać do serwera o wykonaniu + if( iPause != Global.iPause ) { // przesłanie informacji o pauzie do programu nadzorującego + multiplayer::WyslijParam( 5, 3 ); // ramka 5 z czasem i stanem zapauzowania + iPause = Global.iPause; + } + + // fixed step part of the camera update + if( ( simulation::Train != nullptr ) + && ( Camera.Type == TCameraType::tp_Follow ) + && ( false == DebugCameraFlag ) ) { + // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery + simulation::Train->UpdateMechPosition( m_secondaryupdaterate ); + } + + m_secondaryupdateaccumulator -= m_secondaryupdaterate; // these should be inexpensive enough we have no cap + } + + // variable step simulation time routines + + if( Global.changeDynObj ) { + // ABu zmiana pojazdu - przejście do innego + ChangeDynamic(); + } + + if( simulation::Train != nullptr ) { + TSubModel::iInstance = reinterpret_cast( simulation::Train->Dynamic() ); + simulation::Train->Update( deltatime ); + } + else { + TSubModel::iInstance = 0; + } + + simulation::Events.update(); + simulation::Region->update_events(); + simulation::Lights.update(); + + // render time routines follow: + + auto const deltarealtime = Timer::GetDeltaRenderTime(); // nie uwzględnia pauzowania ani mnożenia czasu + + // fixed step render time routines + + fTime50Hz += deltarealtime; // w pauzie też trzeba zliczać czas, bo przy dużym FPS będzie problem z odczytem ramek + while( fTime50Hz >= 1.0 / 50.0 ) { + Console::Update(); // to i tak trzeba wywoływać + ui::Transcripts.Update(); // obiekt obsługujący stenogramy dźwięków na ekranie + m_userinterface->update(); + // decelerate camera + Camera.Velocity *= 0.65; + if( std::abs( Camera.Velocity.x ) < 0.01 ) { Camera.Velocity.x = 0.0; } + if( std::abs( Camera.Velocity.y ) < 0.01 ) { Camera.Velocity.y = 0.0; } + if( std::abs( Camera.Velocity.z ) < 0.01 ) { Camera.Velocity.z = 0.0; } + // decelerate debug camera too + DebugCamera.Velocity *= 0.65; + if( std::abs( DebugCamera.Velocity.x ) < 0.01 ) { DebugCamera.Velocity.x = 0.0; } + if( std::abs( DebugCamera.Velocity.y ) < 0.01 ) { DebugCamera.Velocity.y = 0.0; } + if( std::abs( DebugCamera.Velocity.z ) < 0.01 ) { DebugCamera.Velocity.z = 0.0; } + + fTime50Hz -= 1.0 / 50.0; + } + + // variable step render time routines + + update_camera( deltarealtime ); + + Timer::subsystem.sim_total.stop(); + + simulation::Region->update_sounds(); + audio::renderer.update( deltarealtime ); + + GfxRenderer.Update( deltarealtime ); + + simulation::is_ready = true; + + m_input.poll(); + + return true; +} + +// maintenance method, called when the mode is activated +void +driver_mode::enter() { + + Camera.Init(Global.FreeCameraInit[0], Global.FreeCameraInitAngle[0], ( FreeFlyModeFlag ? TCameraType::tp_Free : TCameraType::tp_Follow ) ); + Global.pCamera = &Camera; + Global.pDebugCamera = &DebugCamera; + + TDynamicObject *nPlayerTrain { ( + ( Global.asHumanCtrlVehicle != "ghostview" ) ? + simulation::Vehicles.find( Global.asHumanCtrlVehicle ) : + nullptr ) }; + + if (nPlayerTrain) + { + WriteLog( "Initializing player train, \"" + Global.asHumanCtrlVehicle + "\"" ); + + if( simulation::Train == nullptr ) { + simulation::Train = new TTrain(); + } + if( simulation::Train->Init( nPlayerTrain ) ) + { + WriteLog("Player train initialization OK"); + + Application.set_title( Global.AppName + " (" + simulation::Train->Controlled()->Name + " @ " + Global.SceneryFile + ")" ); + + FollowView(); + } + else + { + Error("Bad init: player train initialization failed"); + FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie + SafeDelete( simulation::Train ); + Camera.Type = TCameraType::tp_Free; + } + } + else + { + if (Global.asHumanCtrlVehicle != "ghostview") + { + Error("Bad scenario: failed to locate player train, \"" + Global.asHumanCtrlVehicle + "\"" ); + } + FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie + Camera.Type = TCameraType::tp_Free; + DebugCamera = Camera; + Global.DebugCameraPosition = DebugCamera.Pos; + } + + // if (!Global.bMultiplayer) //na razie włączone + { // eventy aktywowane z klawiatury tylko dla jednego użytkownika + KeyEvents[ 0 ] = simulation::Events.FindEvent( "keyctrl00" ); + KeyEvents[ 1 ] = simulation::Events.FindEvent( "keyctrl01" ); + KeyEvents[ 2 ] = simulation::Events.FindEvent( "keyctrl02" ); + KeyEvents[ 3 ] = simulation::Events.FindEvent( "keyctrl03" ); + KeyEvents[ 4 ] = simulation::Events.FindEvent( "keyctrl04" ); + KeyEvents[ 5 ] = simulation::Events.FindEvent( "keyctrl05" ); + KeyEvents[ 6 ] = simulation::Events.FindEvent( "keyctrl06" ); + KeyEvents[ 7 ] = simulation::Events.FindEvent( "keyctrl07" ); + KeyEvents[ 8 ] = simulation::Events.FindEvent( "keyctrl08" ); + KeyEvents[ 9 ] = simulation::Events.FindEvent( "keyctrl09" ); + } + + Timer::ResetTimers(); + + Application.set_cursor( GLFW_CURSOR_DISABLED ); + Application.set_cursor_pos( 0, 0 ); + Global.ControlPicking = false; +} + +// maintenance method, called when the mode is deactivated +void +driver_mode::exit() { + +} + +void +driver_mode::on_key( int const Key, int const Scancode, int const Action, int const 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 == m_userinterface->on_key( Key, Action ) ) { return; } + // ...if the input is left untouched, pass it on + if( true == m_input.keyboard.key( Key, Action ) ) { return; } + + 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_PRESS ) { + + if( Global.ControlPicking ) { + // switch off + Application.get_cursor_pos( m_input.mouse_pickmodepos.x, m_input.mouse_pickmodepos.y ); + Application.set_cursor( GLFW_CURSOR_DISABLED ); + Application.set_cursor_pos( 0, 0 ); + } + else { + // enter picking mode + Application.set_cursor_pos( m_input.mouse_pickmodepos.x, m_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 ) { + + OnKeyDown( Key ); + +#ifdef CAN_I_HAS_LIBPNG + switch( key ) { + case GLFW_KEY_PRINT_SCREEN: { + make_screenshot(); + break; + } + default: { break; } + } +#endif + } +} + +void +driver_mode::on_cursor_pos( double const Horizontal, double const Vertical ) { + + if( false == Global.ControlPicking ) { + // in regular view mode keep cursor on screen + Application.set_cursor_pos( 0, 0 ); + } + // give the potential event recipient a shot at it, in the virtual z order + m_input.mouse.move( Horizontal, Vertical ); +} + +void +driver_mode::on_mouse_button( int const Button, int const Action, int const Mods ) { + + // give the potential event recipient a shot at it, in the virtual z order + m_input.mouse.button( Button, Action ); +} + +void +driver_mode::on_scroll( double const Xoffset, double const Yoffset ) { + + if( Global.ctrlState ) { + // ctrl + scroll wheel adjusts fov + Global.FieldOfView = clamp( static_cast( Global.FieldOfView - Yoffset * 20.0 / Timer::subsystem.gfx_total.average() ), 15.0f, 75.0f ); + } +} + +void +driver_mode::update_camera( double const Deltatime ) { + + auto *controlled = ( + simulation::Train ? + simulation::Train->Dynamic() : + nullptr ); + + if( false == Global.ControlPicking ) { + if( m_input.mouse.button( GLFW_MOUSE_BUTTON_LEFT ) == GLFW_PRESS ) { + Camera.Reset(); // likwidacja obrotów - patrzy horyzontalnie na południe + if( controlled && LengthSquared3( controlled->GetPosition() - Camera.Pos ) < ( 1500 * 1500 ) ) { + // gdy bliżej niż 1.5km + Camera.LookAt = controlled->GetPosition(); + } + else { + TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global.pCameraPosition, 300, false, false ) ); + if( !d ) + d = std::get( simulation::Region->find_vehicle( Global.pCameraPosition, 1000, false, false ) ); // dalej szukanie, jesli bliżej nie ma + + if( d && pDynamicNearest ) { + // jeśli jakiś jest znaleziony wcześniej + if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { + d = pDynamicNearest; // jeśli najbliższy nie jest 10 razy bliżej niż + } + } + // poprzedni najbliższy, zostaje poprzedni + if( d ) + pDynamicNearest = d; // zmiana na nowy, jeśli coś znaleziony niepusty + if( pDynamicNearest ) + Camera.LookAt = pDynamicNearest->GetPosition(); + } + if( FreeFlyModeFlag ) + Camera.RaLook(); // jednorazowe przestawienie kamery + } + else if( m_input.mouse.button( GLFW_MOUSE_BUTTON_RIGHT ) == GLFW_PRESS ) { + FollowView( false ); // bez wyciszania dźwięków + } + } + + if( m_input.mouse.button( GLFW_MOUSE_BUTTON_MIDDLE ) == GLFW_PRESS ) { + // middle mouse button controls zoom. + Global.ZoomFactor = std::min( 4.5f, Global.ZoomFactor + 15.0f * static_cast( Deltatime ) ); + } + else if( m_input.mouse.button( GLFW_MOUSE_BUTTON_MIDDLE ) != GLFW_PRESS ) { + // reset zoom level if the button is no longer held down. + // NOTE: yes, this is terrible way to go about it. it'll do for now. + Global.ZoomFactor = std::max( 1.0f, Global.ZoomFactor - 15.0f * static_cast( Deltatime ) ); + } + + if( DebugCameraFlag ) { DebugCamera.Update(); } + else { Camera.Update(); } // uwzględnienie ruchu wywołanego klawiszami + + if( ( false == FreeFlyModeFlag ) + && ( false == Global.CabWindowOpen ) + && ( simulation::Train != nullptr ) ) { + // cache cab camera view angles in case of view type switch + simulation::Train->pMechViewAngle = { Camera.Pitch, Camera.Yaw }; + } + + // reset window state, it'll be set again if applicable in a check below + Global.CabWindowOpen = false; + + if( ( simulation::Train != nullptr ) + && ( Camera.Type == TCameraType::tp_Follow ) + && ( false == DebugCameraFlag ) ) { + // jeśli jazda w kabinie, przeliczyć trzeba parametry kamery +/* + auto tempangle = controlled->VectorFront() * ( controlled->MoverParameters->ActiveCab == -1 ? -1 : 1 ); + double modelrotate = atan2( -tempangle.x, tempangle.z ); +*/ + if( ( true == Global.ctrlState ) + && ( ( m_input.keyboard.key( GLFW_KEY_LEFT ) != GLFW_RELEASE ) + || ( m_input.keyboard.key( GLFW_KEY_RIGHT ) != GLFW_RELEASE ) ) ) { + // jeśli lusterko lewe albo prawe (bez rzucania na razie) + Global.CabWindowOpen = true; + + auto const lr { m_input.keyboard.key( GLFW_KEY_LEFT ) != GLFW_RELEASE }; + // Camera.Yaw powinno być wyzerowane, aby po powrocie patrzeć do przodu + Camera.Pos = controlled->GetPosition() + simulation::Train->MirrorPosition( lr ); // pozycja lusterka + Camera.Yaw = 0; // odchylenie na bok od Camera.LookAt + if( simulation::Train->Occupied()->ActiveCab == 0 ) + Camera.LookAt = Camera.Pos - simulation::Train->GetDirection(); // gdy w korytarzu + else if( Global.shiftState ) { + // patrzenie w bok przez szybę + Camera.LookAt = Camera.Pos - ( lr ? -1 : 1 ) * controlled->VectorLeft() * simulation::Train->Occupied()->ActiveCab; + } + else { // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny - jakby z lusterka, + // ale bez odbicia + Camera.LookAt = Camera.Pos - simulation::Train->GetDirection() * simulation::Train->Occupied()->ActiveCab; //-1 albo 1 + } + Camera.Roll = std::atan( simulation::Train->pMechShake.x * simulation::Train->fMechRoll ); // hustanie kamery na boki + Camera.Pitch = 0.5 * std::atan( simulation::Train->vMechVelocity.z * simulation::Train->fMechPitch ); // hustanie kamery przod tyl + Camera.vUp = controlled->VectorUp(); + } + else { + // patrzenie standardowe + // potentially restore view angle after returning from external view + // TODO: mirror view toggle as separate method + Camera.Pitch = simulation::Train->pMechViewAngle.x; + Camera.Yaw = simulation::Train->pMechViewAngle.y; + + Camera.Pos = simulation::Train->GetWorldMechPosition(); // Train.GetPosition1(); + if( !Global.iPause ) { + // podczas pauzy nie przeliczać kątów przypadkowymi wartościami + // hustanie kamery na boki + Camera.Roll = atan( simulation::Train->vMechVelocity.x * simulation::Train->fMechRoll ); + // hustanie kamery przod tyl + // Ra: tu jest uciekanie kamery w górę!!! + Camera.Pitch -= 0.5 * atan( simulation::Train->vMechVelocity.z * simulation::Train->fMechPitch ); + } +/* + // ABu011104: rzucanie pudlem + vector3 temp; + if( abs( Train->pMechShake.y ) < 0.25 ) + temp = vector3( 0, 0, 6 * Train->pMechShake.y ); + else if( ( Train->pMechShake.y ) > 0 ) + temp = vector3( 0, 0, 6 * 0.25 ); + else + temp = vector3( 0, 0, -6 * 0.25 ); + if( Controlled ) + Controlled->ABuSetModelShake( temp ); + // ABu: koniec rzucania +*/ + if( simulation::Train->Occupied()->ActiveCab == 0 ) + Camera.LookAt = simulation::Train->GetWorldMechPosition() + simulation::Train->GetDirection() * 5.0; // gdy w korytarzu + else // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny + Camera.LookAt = simulation::Train->GetWorldMechPosition() + simulation::Train->GetDirection() * 5.0 * simulation::Train->Occupied()->ActiveCab; //-1 albo 1 + Camera.vUp = simulation::Train->GetUp(); + } + } + // all done, update camera position to the new value + Global.pCameraPosition = Camera.Pos; + Global.DebugCameraPosition = DebugCamera.Pos; +} + +void +driver_mode::OnKeyDown(int cKey) { + // dump keypress info in the log + // podczas pauzy klawisze nie działają + std::string keyinfo; + auto keyname = glfwGetKeyName( cKey, 0 ); + if( keyname != nullptr ) { + keyinfo += std::string( keyname ); + } + else { + switch( cKey ) { + + case GLFW_KEY_SPACE: { keyinfo += "Space"; break; } + case GLFW_KEY_ENTER: { keyinfo += "Enter"; break; } + case GLFW_KEY_ESCAPE: { keyinfo += "Esc"; break; } + case GLFW_KEY_TAB: { keyinfo += "Tab"; break; } + case GLFW_KEY_INSERT: { keyinfo += "Insert"; break; } + case GLFW_KEY_DELETE: { keyinfo += "Delete"; break; } + case GLFW_KEY_HOME: { keyinfo += "Home"; break; } + case GLFW_KEY_END: { keyinfo += "End"; break; } + case GLFW_KEY_F1: { keyinfo += "F1"; break; } + case GLFW_KEY_F2: { keyinfo += "F2"; break; } + case GLFW_KEY_F3: { keyinfo += "F3"; break; } + case GLFW_KEY_F4: { keyinfo += "F4"; break; } + case GLFW_KEY_F5: { keyinfo += "F5"; break; } + case GLFW_KEY_F6: { keyinfo += "F6"; break; } + case GLFW_KEY_F7: { keyinfo += "F7"; break; } + case GLFW_KEY_F8: { keyinfo += "F8"; break; } + case GLFW_KEY_F9: { keyinfo += "F9"; break; } + case GLFW_KEY_F10: { keyinfo += "F10"; break; } + case GLFW_KEY_F11: { keyinfo += "F11"; break; } + case GLFW_KEY_F12: { keyinfo += "F12"; break; } + case GLFW_KEY_PAUSE: { keyinfo += "Pause"; break; } + } + } + if( keyinfo.empty() == false ) { + + std::string keymodifiers; + if( Global.shiftState ) + keymodifiers += "[Shift]+"; + if( Global.ctrlState ) + keymodifiers += "[Ctrl]+"; + + WriteLog( "Key pressed: " + keymodifiers + "[" + keyinfo + "]" ); + } + + // actual key processing + // TODO: redo the input system + if( ( cKey >= GLFW_KEY_0 ) && ( cKey <= GLFW_KEY_9 ) ) // klawisze cyfrowe + { + int i = cKey - GLFW_KEY_0; // numer klawisza + if (Global.shiftState) { + // z [Shift] uruchomienie eventu + if( ( false == Global.iPause ) // podczas pauzy klawisze nie działają + && ( KeyEvents[ i ] != nullptr ) ) { + simulation::Events.AddToQuery( KeyEvents[ i ], NULL ); + } + } + else // zapamiętywanie kamery może działać podczas pauzy + if( FreeFlyModeFlag ) { + // w trybie latania można przeskakiwać do ustawionych kamer + if( ( !Global.FreeCameraInit[ i ].x ) + && ( !Global.FreeCameraInit[ i ].y ) + && ( !Global.FreeCameraInit[ i ].z ) ) { + // jeśli kamera jest w punkcie zerowym, zapamiętanie współrzędnych i kątów + Global.FreeCameraInit[ i ] = Camera.Pos; + Global.FreeCameraInitAngle[ i ].x = Camera.Pitch; + Global.FreeCameraInitAngle[ i ].y = Camera.Yaw; + Global.FreeCameraInitAngle[ i ].z = Camera.Roll; + // logowanie, żeby można było do scenerii przepisać + WriteLog( + "camera " + std::to_string( Global.FreeCameraInit[ i ].x ) + " " + + std::to_string( Global.FreeCameraInit[ i ].y ) + " " + + std::to_string( Global.FreeCameraInit[ i ].z ) + " " + + std::to_string( RadToDeg( Global.FreeCameraInitAngle[ i ].x ) ) + " " + + std::to_string( RadToDeg( Global.FreeCameraInitAngle[ i ].y ) ) + " " + + std::to_string( RadToDeg( Global.FreeCameraInitAngle[ i ].z ) ) + " " + + std::to_string( i ) + " endcamera" ); + } + else // również przeskakiwanie + { // Ra: to z tą kamerą (Camera.Pos i Global.pCameraPosition) jest trochę bez sensu + Global.pCameraPosition = Global.FreeCameraInit[ i ]; // nowa pozycja dla generowania obiektów + Camera.Init( Global.FreeCameraInit[ i ], Global.FreeCameraInitAngle[ i ], TCameraType::tp_Free ); // przestawienie + } + } + // będzie jeszcze załączanie sprzęgów z [Ctrl] + } + else if( ( cKey >= GLFW_KEY_F1 ) && ( cKey <= GLFW_KEY_F12 ) ) + { + switch (cKey) { + + case GLFW_KEY_F1: { + + if( DebugModeFlag ) { + // additional simulation clock jump keys in debug mode + if( Global.ctrlState ) { + // ctrl-f1 + simulation::Time.update( 20.0 * 60.0 ); + } + else if( Global.shiftState ) { + // shift-f1 + simulation::Time.update( 5.0 * 60.0 ); + } + } + break; + } + + case GLFW_KEY_F4: { + + InOutKey( !Global.shiftState ); // distant view with Shift, short distance step out otherwise + break; + } + case GLFW_KEY_F5: { + // przesiadka do innego pojazdu + if( false == FreeFlyModeFlag ) { + // only available in free fly mode + break; + } + + TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global.pCameraPosition, 50, true, false ) ); + + if( tmp != nullptr ) { + + if( simulation::Train ) {// jeśli mielismy pojazd + if( simulation::Train->Dynamic()->Mechanik ) { // na skutek jakiegoś błędu może czasem zniknąć + simulation::Train->Dynamic()->Mechanik->TakeControl( true ); // oddajemy dotychczasowy AI + } + } + + if( ( true == DebugModeFlag ) + || ( tmp->MoverParameters->Vel <= 5.0 ) ) { + // works always in debug mode, or for stopped/slow moving vehicles otherwise + if( simulation::Train == nullptr ) { + simulation::Train = new TTrain(); // jeśli niczym jeszcze nie jeździlismy + } + if( simulation::Train->Init( tmp ) ) { // przejmujemy sterowanie + simulation::Train->Dynamic()->Mechanik->TakeControl( false ); + } + else { + SafeDelete( simulation::Train ); // i nie ma czym sterować + } + if( simulation::Train ) { + InOutKey(); // do kabiny + } + } + } + break; + } + case GLFW_KEY_F6: { + // przyspieszenie symulacji do testowania scenerii... uwaga na FPS! + if( DebugModeFlag ) { + + if( Global.ctrlState ) { Global.fTimeSpeed = ( Global.shiftState ? 60.0 : 20.0 ); } + else { Global.fTimeSpeed = ( Global.shiftState ? 5.0 : 1.0 ); } + } + break; + } + case GLFW_KEY_F7: { + // debug mode functions + if( DebugModeFlag ) { + + if( Global.ctrlState + && Global.shiftState ) { + // shift + ctrl + f7 toggles between debug and regular camera + DebugCameraFlag = !DebugCameraFlag; + } + else if( Global.ctrlState ) { + // ctrl + f7 toggles static daylight + simulation::Environment.toggle_daylight(); + break; + } + else if( Global.shiftState ) { + // shift + f7 is currently unused + } + else { + // f7: wireframe toggle + // TODO: pass this to renderer instead of making direct calls + Global.bWireFrame = !Global.bWireFrame; + if( true == Global.bWireFrame ) { + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + } + else { + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + } + } + break; + } + case GLFW_KEY_F11: { + // editor mode + if( ( false == Global.ctrlState ) + && ( false == Global.shiftState ) ) { + Application.push_mode( eu07_application::mode::editor ); + } + break; + } + case GLFW_KEY_F12: { + // quick debug mode toggle + if( Global.ctrlState + && Global.shiftState ) { + DebugModeFlag = !DebugModeFlag; + } + break; + } + + default: { + break; + } + } + // if (cKey!=VK_F4) + return; // nie są przekazywane do pojazdu wcale + } + + if ((Global.iTextMode == GLFW_KEY_F12) ? (cKey >= '0') && (cKey <= '9') : false) + { // tryb konfiguracji debugmode (przestawianie kamery już wyłączone + if (!Global.shiftState) // bez [Shift] + { + if (cKey == GLFW_KEY_1) + Global.iWriteLogEnabled ^= 1; // włącz/wyłącz logowanie do pliku + else if (cKey == GLFW_KEY_2) + { // włącz/wyłącz okno konsoli + Global.iWriteLogEnabled ^= 2; + if ((Global.iWriteLogEnabled & 2) == 0) // nie było okienka + { // otwarcie okna + AllocConsole(); // jeśli konsola już jest, to zwróci błąd; uwalniać nie ma po + // co, bo się odłączy + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN); + } + } + // else if (cKey=='3') Global.iWriteLogEnabled^=4; //wypisywanie nazw torów + } + } + else if( cKey == GLFW_KEY_ESCAPE ) { + // toggle pause + if( Global.iPause & 1 ) // jeśli pauza startowa + Global.iPause &= ~1; // odpauzowanie, gdy po wczytaniu miało nie startować + else if( !( Global.iMultiplayer & 2 ) ) // w multiplayerze pauza nie ma sensu + Global.iPause ^= 2; // zmiana stanu zapauzowania + if( Global.iPause ) {// jak pauza + Global.iTextMode = GLFW_KEY_F1; // to wyświetlić zegar i informację + } + } + else { + + if( ( true == DebugModeFlag ) + && ( false == Global.shiftState ) + && ( true == Global.ctrlState ) + && ( simulation::Train != nullptr ) + && ( simulation::Train->Dynamic()->Controller == Humandriver ) ) { + + if( DebugModeFlag ) { + // przesuwanie składu o 100m + auto *vehicle { simulation::Train->Dynamic() }; + TDynamicObject *d = vehicle; + if( cKey == GLFW_KEY_LEFT_BRACKET ) { + while( d ) { + d->Move( 100.0 * d->DirectionGet() ); + d = d->Next(); // pozostałe też + } + d = vehicle->Prev(); + while( d ) { + d->Move( 100.0 * d->DirectionGet() ); + d = d->Prev(); // w drugą stronę też + } + } + else if( cKey == GLFW_KEY_RIGHT_BRACKET ) { + while( d ) { + d->Move( -100.0 * d->DirectionGet() ); + d = d->Next(); // pozostałe też + } + d = vehicle->Prev(); + while( d ) { + d->Move( -100.0 * d->DirectionGet() ); + d = d->Prev(); // w drugą stronę też + } + } + else if( cKey == GLFW_KEY_TAB ) { + while( d ) { + d->MoverParameters->V += d->DirectionGet()*2.78; + d = d->Next(); // pozostałe też + } + d = vehicle->Prev(); + while( d ) { + d->MoverParameters->V += d->DirectionGet()*2.78; + d = d->Prev(); // w drugą stronę też + } + } + } + } + } +} + +// places camera outside the controlled vehicle, or nearest if nothing is under control +// depending on provided switch the view is placed right outside, or at medium distance +void +driver_mode::DistantView( bool const Near ) { + + TDynamicObject const *vehicle = { ( + ( simulation::Train != nullptr ) ? + simulation::Train->Dynamic() : + pDynamicNearest ) }; + + if( vehicle == nullptr ) { return; } + + auto const cab = + ( vehicle->MoverParameters->ActiveCab == 0 ? + 1 : + vehicle->MoverParameters->ActiveCab ); + auto const left = vehicle->VectorLeft() * cab; + + if( true == Near ) { + + Camera.Pos = + Math3D::vector3( Camera.Pos.x, vehicle->GetPosition().y, Camera.Pos.z ) + + left * vehicle->GetWidth() + + Math3D::vector3( 1.25 * left.x, 1.6, 1.25 * left.z ); + } + else { + + Camera.Pos = + vehicle->GetPosition() + + vehicle->VectorFront() * vehicle->MoverParameters->ActiveCab * 50.0 + + Math3D::vector3( -10.0 * left.x, 1.6, -10.0 * left.z ); + } + + Camera.LookAt = vehicle->GetPosition(); + Camera.RaLook(); // jednorazowe przestawienie kamery +} + +// ustawienie śledzenia pojazdu +void +driver_mode::FollowView(bool wycisz) { + + Camera.Reset(); // likwidacja obrotów - patrzy horyzontalnie na południe + + auto *train { simulation::Train }; + + if (train != nullptr ) // jest pojazd do prowadzenia? + { + if (FreeFlyModeFlag) + { // jeżeli poza kabiną, przestawiamy w jej okolicę - OK + // wyłączenie trzęsienia na siłę? + train->Dynamic()->ABuSetModelShake( {} ); + + DistantView(); // przestawienie kamery + //żeby nie bylo numerów z 'fruwajacym' lokiem - konsekwencja bujania pudła + // tu ustawić nową, bo od niej liczą się odległości + Global.pCameraPosition = Camera.Pos; + } + else { + Camera.Pos = train->pMechPosition; + // potentially restore cached camera angles + Camera.Pitch = train->pMechViewAngle.x; + Camera.Yaw = train->pMechViewAngle.y; + + Camera.Roll = std::atan(train->pMechShake.x * train->fMechRoll); // hustanie kamery na boki + Camera.Pitch -= 0.5 * std::atan(train->vMechVelocity.z * train->fMechPitch); // hustanie kamery przod tyl + + if( train->Occupied()->ActiveCab == 0 ) { + Camera.LookAt = + train->pMechPosition + + train->GetDirection() * 5.0; + } + else { + // patrz w strone wlasciwej kabiny + Camera.LookAt = + train->pMechPosition + + train->GetDirection() * 5.0 * train->Occupied()->ActiveCab; + } + train->pMechOffset = train->pMechSittingPosition; + } + } + else + DistantView(); +} + +void +driver_mode::ChangeDynamic() { + + auto *train { simulation::Train }; + if( train == nullptr ) { return; } + + auto *vehicle { train->Dynamic() }; + auto *occupied { train->Occupied() }; + auto *driver { vehicle->Mechanik }; + // Ra: to nie może być tak robione, to zbytnia proteza jest + if( driver ) { + // AI może sobie samo pójść + if( false == driver->AIControllFlag ) { + // tylko jeśli ręcznie prowadzony + // jeśli prowadzi AI, to mu nie robimy dywersji! + occupied->CabDeactivisation(); + occupied->ActiveCab = 0; + occupied->BrakeLevelSet( occupied->Handle->GetPos( bh_NP ) ); //rozwala sterowanie hamulcem GF 04-2016 + vehicle->MechInside = false; + vehicle->Controller = AIdriver; + } + } + TDynamicObject *temp = Global.changeDynObj; + vehicle->bDisplayCab = false; + vehicle->ABuSetModelShake( {} ); + + if( driver ) // AI może sobie samo pójść + if( false == driver->AIControllFlag ) { + // tylko jeśli ręcznie prowadzony + // przsunięcie obiektu zarządzającego + driver->MoveTo( temp ); + } + + train->DynamicSet( temp ); + // update helpers + train = simulation::Train; + vehicle = train->Dynamic(); + occupied = train->Occupied(); + driver = vehicle->Mechanik; + Global.asHumanCtrlVehicle = vehicle->name(); + if( driver ) // AI może sobie samo pójść + if( false == driver->AIControllFlag ) // tylko jeśli ręcznie prowadzony + { + occupied->LimPipePress = occupied->PipePress; + occupied->CabActivisation(); // załączenie rozrządu (wirtualne kabiny) + vehicle->MechInside = true; + vehicle->Controller = Humandriver; + } + train->InitializeCab( + occupied->CabNo, + vehicle->asBaseDir + occupied->TypeName + ".mmd" ); + if( false == FreeFlyModeFlag ) { + vehicle->bDisplayCab = true; + vehicle->ABuSetModelShake( {} ); // zerowanie przesunięcia przed powrotem? + train->MechStop(); + FollowView(); // na pozycję mecha + } + Global.changeDynObj = nullptr; +} + +void +driver_mode::InOutKey( bool const Near ) +{ // przełączenie widoku z kabiny na zewnętrzny i odwrotnie + FreeFlyModeFlag = !FreeFlyModeFlag; // zmiana widoku + + auto *train { simulation::Train }; + + if (FreeFlyModeFlag) { + // jeżeli poza kabiną, przestawiamy w jej okolicę - OK + if (train) { + // cache current cab position so there's no need to set it all over again after each out-in switch + train->pMechSittingPosition = train->pMechOffset; + + train->Dynamic()->bDisplayCab = false; + DistantView( Near ); + } + DebugCamera = Camera; + Global.DebugCameraPosition = DebugCamera.Pos; + } + else + { // jazda w kabinie + if (train) + { + train->Dynamic()->bDisplayCab = true; + // zerowanie przesunięcia przed powrotem? + train->Dynamic()->ABuSetModelShake( { 0, 0, 0 } ); + train->MechStop(); + FollowView(); // na pozycję mecha + } + else + FreeFlyModeFlag = true; // nadal poza kabiną + } + // update window title to reflect the situation + Application.set_title( Global.AppName + " (" + ( train != nullptr ? train->Occupied()->Name : "" ) + " @ " + Global.SceneryFile + ")" ); +} diff --git a/drivermode.h b/drivermode.h new file mode 100644 index 00000000..81085a66 --- /dev/null +++ b/drivermode.h @@ -0,0 +1,87 @@ +/* +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 + +#include "applicationmode.h" + +#include "driverkeyboardinput.h" +#include "drivermouseinput.h" +#include "gamepadinput.h" +#include "uart.h" +#include "console.h" +#include "camera.h" +#include "classes.h" + +class driver_mode : public application_mode { + +public: +// constructors + driver_mode(); + +// methods + // initializes internal data structures of the mode. returns: true on success, false otherwise + bool + init() override; + // mode-specific update of simulation data. returns: false on error, true otherwise + bool + update() override; + // maintenance method, called when the mode is activated + void + enter() override; + // maintenance method, called when the mode is deactivated + void + exit() override; + // input handlers + void + on_key( int const Key, int const Scancode, int const Action, int const Mods ) override; + void + on_cursor_pos( double const Horizontal, double const Vertical ) override; + void + on_mouse_button( int const Button, int const Action, int const Mods ) override; + void + on_scroll( double const Xoffset, double const Yoffset ) override; + +private: +// types + struct drivermode_input { + + gamepad_input gamepad; + mouse_input mouse; + glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode + driverkeyboard_input keyboard; + Console console; + std::unique_ptr uart; + + bool init(); + void poll(); + }; + +// methods + void update_camera( const double Deltatime ); + // handles vehicle change flag + void OnKeyDown( int cKey ); + void ChangeDynamic(); + void InOutKey( bool const Near = true ); + void FollowView( bool wycisz = true ); + void DistantView( bool const Near = false ); + +// members + drivermode_input m_input; + std::array KeyEvents { nullptr }; // eventy wyzwalane z klawiaury + TCamera Camera; + TCamera DebugCamera; + TDynamicObject *pDynamicNearest { nullptr }; // vehicle nearest to the active camera. TODO: move to camera + double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys + double const m_primaryupdaterate { 1.0 / 100.0 }; + double const m_secondaryupdaterate { 1.0 / 50.0 }; + double m_primaryupdateaccumulator { m_secondaryupdaterate }; // keeps track of elapsed simulation time, for core fixed step routines + double m_secondaryupdateaccumulator { m_secondaryupdaterate }; // keeps track of elapsed simulation time, for less important fixed step routines + int iPause { 0 }; // wykrywanie zmian w zapauzowaniu +}; diff --git a/mouseinput.cpp b/drivermouseinput.cpp similarity index 98% rename from mouseinput.cpp rename to drivermouseinput.cpp index cc806416..2035b896 100644 --- a/mouseinput.cpp +++ b/drivermouseinput.cpp @@ -8,15 +8,15 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "mouseinput.h" +#include "drivermouseinput.h" #include "application.h" #include "utilities.h" #include "globals.h" #include "timer.h" #include "simulation.h" -#include "world.h" #include "train.h" +#include "animmodel.h" #include "renderer.h" #include "uilayer.h" @@ -25,7 +25,7 @@ mouse_slider::bind( user_command const &Command ) { m_command = Command; - auto const *train { World.train() }; + auto const *train { simulation::Train }; TMoverParameters const *vehicle { nullptr }; switch( m_command ) { case user_command::mastercontrollerset: @@ -184,6 +184,11 @@ mouse_input::move( double Mousex, double Mousey ) { void mouse_input::button( int const Button, int const Action ) { + // store key state + if( Button >= 0 ) { + m_buttons[ Button ] = Action; + } + if( false == Global.ControlPicking ) { return; } if( true == FreeFlyModeFlag ) { @@ -236,7 +241,7 @@ mouse_input::button( int const Button, int const Action ) { } else { // if not release then it's press - auto const lookup = m_mousecommands.find( World.train()->GetLabel( GfxRenderer.Update_Pick_Control() ) ); + auto const lookup = m_mousecommands.find( simulation::Train->GetLabel( GfxRenderer.Update_Pick_Control() ) ); if( lookup != m_mousecommands.end() ) { // if the recognized element under the cursor has a command associated with the pressed button, notify the recipient mousecommand = ( @@ -301,6 +306,12 @@ mouse_input::button( int const Button, int const Action ) { } } +int +mouse_input::button( int const Button ) const { + + return m_buttons[ Button ]; +} + void mouse_input::poll() { diff --git a/mouseinput.h b/drivermouseinput.h similarity index 95% rename from mouseinput.h rename to drivermouseinput.h index c0fa2cb2..4e06c6b4 100644 --- a/mouseinput.h +++ b/drivermouseinput.h @@ -53,6 +53,8 @@ public: init(); void button( int const Button, int const Action ); + int + button( int const Button ) const; void move( double const Mousex, double const Mousey ); void @@ -87,6 +89,7 @@ private: glm::dvec2 m_cursorposition; // stored last cursor position, used for panning bool m_varyingpollrate { false }; // indicates rate of command repeats is affected by the cursor position glm::dvec2 m_varyingpollrateorigin; // helper, cursor position when the command was initiated + std::array m_buttons; }; //--------------------------------------------------------------------------- diff --git a/driveruilayer.cpp b/driveruilayer.cpp new file mode 100644 index 00000000..e103ac42 --- /dev/null +++ b/driveruilayer.cpp @@ -0,0 +1,904 @@ +/* +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 "driveruilayer.h" + +#include "globals.h" +#include "translation.h" +#include "simulation.h" +#include "simulationtime.h" +#include "event.h" +#include "camera.h" +#include "mtable.h" +#include "train.h" +#include "driver.h" +#include "animmodel.h" +#include "dynobj.h" +#include "model3d.h" +#include "renderer.h" +#include "utilities.h" +#include "logs.h" + +driver_ui::driver_ui() { + + clear_texts(); +/* + UIHeader = std::make_shared( 20, 20 ); // header ui panel + UITable = std::make_shared( 20, 100 ); // schedule or scan table + UITranscripts = std::make_shared( 85, 600 ); // voice transcripts +*/ + // make 4 empty lines for the ui header, to cut down on work down the road + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + // bind the panels with ui object. maybe not the best place for this but, eh + push_back( &UIHeader ); + push_back( &UITable ); + push_back( &UITranscripts ); +} + +// potentially processes provided input key. returns: true if key was processed, false otherwise +bool +driver_ui::on_key( int const Key, int const Action ) { + // TODO: pass the input first through an active ui element if there's any + // if the ui element shows no interest or we don't have one, try to interpret the input yourself: + // shared conditions + switch( Key ) { + + case GLFW_KEY_F1: + case GLFW_KEY_F2: + case GLFW_KEY_F3: + case GLFW_KEY_F8: + case GLFW_KEY_F9: + case GLFW_KEY_F10: + case GLFW_KEY_F12: { // ui mode selectors + + if( ( true == Global.ctrlState ) + || ( true == Global.shiftState ) ) { + // only react to keys without modifiers + return false; + } + + if( Action == GLFW_RELEASE ) { return true; } // recognized, but ignored +/* + EditorModeFlag = ( Key == GLFW_KEY_F11 ); + if( ( true == EditorModeFlag ) + && ( false == Global.ControlPicking ) ) { + set_cursor( GLFW_CURSOR_NORMAL ); + Global.ControlPicking = true; + } +*/ + } + + default: { // everything else + break; + } + } + + switch (Key) { + + case GLFW_KEY_F1: { + // basic consist info + if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; } + if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) { + // wyłączenie napisów + Global.iTextMode = 0; + Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0; + } + else { + Global.iTextMode = Key; + } + return true; + } + + case GLFW_KEY_F2: { + // parametry pojazdu + if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; } + if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) { + // wyłączenie napisów + Global.iTextMode = 0; + Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0; + } + else { + Global.iTextMode = Key; + } + return true; + } + + case GLFW_KEY_F3: { + // timetable + if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; } + if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) { + // wyłączenie napisów + Global.iTextMode = 0; + Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0; + } + else { + Global.iTextMode = Key; + } + return true; + } + + case GLFW_KEY_F8: { + // renderer debug data + Global.iTextMode = Key; + return true; + } + + case GLFW_KEY_F9: { + // wersja + Global.iTextMode = Key; + return true; + } + + case GLFW_KEY_F10: { + // quit + if( Global.iTextMode == Key ) { + Global.iTextMode = + ( Global.iPause && ( Key != GLFW_KEY_F1 ) ? + GLFW_KEY_F1 : + 0 ); // wyłączenie napisów, chyba że pauza + } + else { + Global.iTextMode = Key; + } + return true; + } +/* + case GLFW_KEY_F11: { + // scenario inspector + Global.iTextMode = Key; + return true; + } +*/ + case GLFW_KEY_F12: { + // coś tam jeszcze + Global.iTextMode = Key; + return true; + } + + case GLFW_KEY_Y: { + // potentially quit + if( Global.iTextMode != GLFW_KEY_F10 ) { return false; } // not in quit mode + + if( Action == GLFW_RELEASE ) { return true; } // recognized, but ignored + + glfwSetWindowShouldClose( m_window, 1 ); + return true; + } + + default: { + break; + } + } + + return false; +} + +// updates state of UI elements +void +driver_ui::update() { + + UITable.text_lines.clear(); + std::string uitextline1, uitextline2, uitextline3, uitextline4; + set_tooltip( "" ); + + auto const *train { simulation::Train }; + auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; + auto const *camera { Global.pCamera }; + + if( ( train != nullptr ) && ( false == FreeFlyModeFlag ) ) { + if( false == DebugModeFlag ) { + // in regular mode show control functions, for defined controls + set_tooltip( locale::label_cab_control( train->GetLabel( GfxRenderer.Pick_Control() ) ) ); + } + else { + // in debug mode show names of submodels, to help with cab setup and/or debugging + auto const cabcontrol = GfxRenderer.Pick_Control(); + set_tooltip( ( cabcontrol ? cabcontrol->pName : "" ) ); + } + } + if( ( true == Global.ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) { + auto const scenerynode = GfxRenderer.Pick_Node(); + set_tooltip( + ( scenerynode ? + scenerynode->name() : + "" ) ); + } + + switch( Global.iTextMode ) { + + case( GLFW_KEY_F1 ) : { + // f1, default mode: current time and timetable excerpt + auto const &time = simulation::Time.data(); + uitextline1 = + "Time: " + + to_string( time.wHour ) + ":" + + ( time.wMinute < 10 ? "0" : "" ) + to_string( time.wMinute ) + ":" + + ( time.wSecond < 10 ? "0" : "" ) + to_string( time.wSecond ); + if( Global.iPause ) { + uitextline1 += " (paused)"; + } + + if( ( controlled != nullptr ) + && ( controlled->Mechanik != nullptr ) ) { + + auto const *mover = controlled->MoverParameters; + auto const *driver = controlled->Mechanik; + + uitextline2 = "Throttle: " + to_string( driver->Controlling()->MainCtrlPos, 0, 2 ) + "+" + std::to_string( driver->Controlling()->ScndCtrlPos ); + if( mover->ActiveDir > 0 ) { uitextline2 += " D"; } + else if( mover->ActiveDir < 0 ) { uitextline2 += " R"; } + else { uitextline2 += " N"; } + + uitextline3 = "Brakes:" + to_string( mover->fBrakeCtrlPos, 1, 5 ) + "+" + to_string( mover->LocalBrakePosA * LocalBrakePosNo, 0 ) + ( mover->SlippingWheels ? " !" : " " ); + + uitextline4 = ( + true == TestFlag( mover->SecuritySystem.Status, s_aware ) ? + "!ALERTER! " : + " " ); + uitextline4 += ( + true == TestFlag( mover->SecuritySystem.Status, s_active ) ? + "!SHP! " : + " " ); + + if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) { + // detail mode on second key press + auto const speedlimit { static_cast( std::floor( driver->VelDesired ) ) }; + uitextline2 += + " Speed: " + std::to_string( static_cast( std::floor( mover->Vel ) ) ) + " km/h" + + " (limit: " + std::to_string( speedlimit ) + " km/h"; + auto const nextspeedlimit { static_cast( std::floor( driver->VelNext ) ) }; + if( nextspeedlimit != speedlimit ) { + uitextline2 += + ", new limit: " + std::to_string( nextspeedlimit ) + " km/h" + + " in " + to_string( driver->ActualProximityDist * 0.001, 1 ) + " km"; + } + uitextline2 += ")"; + auto const reverser { ( mover->ActiveDir > 0 ? 1 : -1 ) }; + auto const grade { controlled->VectorFront().y * 100 * ( controlled->DirectionGet() == reverser ? 1 : -1 ) * reverser }; + if( std::abs( grade ) >= 0.25 ) { + uitextline2 += " Grade: " + to_string( grade, 1 ) + "%"; + } + uitextline3 += + " Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa" + + " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)"; + + auto const stoptime { static_cast( -1.0 * controlled->Mechanik->fStopTime ) }; + if( stoptime > 0 ) { + uitextline4 += " Loading/unloading in progress (" + to_string( stoptime ) + ( stoptime > 1 ? " seconds" : " second" ) + " left)"; + } + else { + auto const trackblockdistance{ std::abs( controlled->Mechanik->TrackBlock() ) }; + if( trackblockdistance <= 75.0 ) { + uitextline4 += " Another vehicle ahead (distance: " + to_string( trackblockdistance, 1 ) + " m)"; + } + } + } + } + + break; + } + + case( GLFW_KEY_F2 ) : { + // timetable + auto *vehicle { + ( FreeFlyModeFlag ? + std::get( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) : + controlled ) }; // w trybie latania lokalizujemy wg mapy + + if( vehicle == nullptr ) { break; } + // if the nearest located vehicle doesn't have a direct driver, try to query its owner + auto const owner = ( + ( ( vehicle->Mechanik != nullptr ) && ( vehicle->Mechanik->Primary() ) ) ? + vehicle->Mechanik : + vehicle->ctOwner ); + if( owner == nullptr ){ break; } + + auto const *table = owner->TrainTimetable(); + if( table == nullptr ) { break; } + + auto const &time = simulation::Time.data(); + uitextline1 = + "Time: " + + to_string( time.wHour ) + ":" + + ( time.wMinute < 10 ? "0" : "" ) + to_string( time.wMinute ) + ":" + + ( time.wSecond < 10 ? "0" : "" ) + to_string( time.wSecond ); + if( Global.iPause ) { + uitextline1 += " (paused)"; + } + + uitextline2 = Bezogonkow( owner->Relation(), true ) + " (" + Bezogonkow( owner->TrainName(), true ) + ")"; + auto const nextstation = Bezogonkow( owner->NextStop(), true ); + if( !nextstation.empty() ) { + // jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania + uitextline3 = " -> " + nextstation; + } + + if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) { + + if( 0 == table->StationCount ) { + // only bother if there's stations to list + UITable.text_lines.emplace_back( "(no timetable)", Global.UITextColor ); + } + else { + // header + UITable.text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor ); + + TMTableLine const *tableline; + for( int i = owner->iStationStart; i <= std::min( owner->iStationStart + 10, table->StationCount ); ++i ) { + // wyświetlenie pozycji z rozkładu + tableline = table->TimeTable + i; // linijka rozkładu + + std::string vmax = + " " + + to_string( tableline->vmax, 0 ); + vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej + std::string const station = ( + Bezogonkow( tableline->StationName, true ) + + " " ) + .substr( 0, 34 ); + std::string const location = ( + ( tableline->km > 0.0 ? + to_string( tableline->km, 2 ) : + "" ) + + " " ) + .substr( 0, 34 - tableline->StationWare.size() ); + std::string const arrival = ( + tableline->Ah >= 0 ? + to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Am ) ).substr( 1, 2 ) : + " | " ); + std::string const departure = ( + tableline->Dh >= 0 ? + to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Dm ) ).substr( 1, 2 ) : + " | " ); + auto const candeparture = ( + ( owner->iStationStart < table->StationIndex ) + && ( i < table->StationIndex ) + && ( ( time.wHour * 60 + time.wMinute ) >= ( tableline->Dh * 60 + tableline->Dm ) ) ); + auto traveltime = + " " + + ( i < 2 ? "" : + tableline->Ah >= 0 ? to_string( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), 0 ) : + to_string( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), 0 ) ); + traveltime = traveltime.substr( traveltime.size() - 3, 3 ); // z wyrównaniem do prawej + + UITable.text_lines.emplace_back( + ( "| " + vmax + " | " + station + " | " + arrival + " | " + traveltime + " |" ), + ( candeparture ? + glm::vec4( 0.0f, 1.0f, 0.0f, 1.0f ) :// czas minął i odjazd był, to nazwa stacji będzie na zielono + Global.UITextColor ) ); + UITable.text_lines.emplace_back( + ( "| | " + location + tableline->StationWare + " | " + departure + " | |" ), + ( candeparture ? + glm::vec4( 0.0f, 1.0f, 0.0f, 1.0f ) :// czas minął i odjazd był, to nazwa stacji będzie na zielono + Global.UITextColor ) ); + // divider/footer + UITable.text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor ); + } + if( owner->iStationStart + 10 < table->StationCount ) { + // if we can't display entire timetable, add a scrolling indicator at the bottom + UITable.text_lines.emplace_back( " ... ", Global.UITextColor ); + } + } + } + + break; + } + + case( GLFW_KEY_F3 ) : { + + auto const *vehicle { + ( FreeFlyModeFlag ? + std::get( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) : + controlled ) }; // w trybie latania lokalizujemy wg mapy + + if( vehicle != nullptr ) { + // jeśli domyślny ekran po pierwszym naciśnięciu + auto const *mover { vehicle->MoverParameters }; + + uitextline1 = "Vehicle name: " + mover->Name; + + if( ( vehicle->Mechanik == nullptr ) && ( vehicle->ctOwner ) ) { + // for cars other than leading unit indicate the leader + uitextline1 += ", owned by " + vehicle->ctOwner->OwnerName(); + } + uitextline1 += "; Status: " + mover->EngineDescription( 0 ); + + // informacja o sprzęgach + uitextline1 += + "; C0:" + + ( vehicle->PrevConnected ? + vehicle->PrevConnected->name() + ":" + to_string( mover->Couplers[ 0 ].CouplingFlag ) + ( + mover->Couplers[ 0 ].CouplingFlag == 0 ? + " (" + to_string( mover->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : + "" ) : + "none" ); + uitextline1 += + " C1:" + + ( vehicle->NextConnected ? + vehicle->NextConnected->name() + ":" + to_string( mover->Couplers[ 1 ].CouplingFlag ) + ( + mover->Couplers[ 1 ].CouplingFlag == 0 ? + " (" + to_string( mover->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : + "" ) : + "none" ); + + // equipment flags + uitextline2 = ( mover->Battery ? "B" : "." ); + uitextline2 += ( mover->Mains ? "M" : "." ); + uitextline2 += ( mover->PantRearUp ? ( mover->PantRearVolt > 0.0 ? "O" : "o" ) : "." ); + uitextline2 += ( mover->PantFrontUp ? ( mover->PantFrontVolt > 0.0 ? "P" : "p" ) : "." ); + uitextline2 += ( mover->PantPressLockActive ? "!" : ( mover->PantPressSwitchActive ? "*" : "." ) ); + uitextline2 += ( mover->WaterPump.is_active ? "W" : ( false == mover->WaterPump.breaker ? "-" : ( mover->WaterPump.is_enabled ? "w" : "." ) ) ); + uitextline2 += ( true == mover->WaterHeater.is_damaged ? "!" : ( mover->WaterHeater.is_active ? "H" : ( false == mover->WaterHeater.breaker ? "-" : ( mover->WaterHeater.is_enabled ? "h" : "." ) ) ) ); + uitextline2 += ( mover->FuelPump.is_active ? "F" : ( mover->FuelPump.is_enabled ? "f" : "." ) ); + uitextline2 += ( mover->OilPump.is_active ? "O" : ( mover->OilPump.is_enabled ? "o" : "." ) ); + uitextline2 += ( false == mover->ConverterAllowLocal ? "-" : ( mover->ConverterAllow ? ( mover->ConverterFlag ? "X" : "x" ) : "." ) ); + uitextline2 += ( mover->ConvOvldFlag ? "!" : "." ); + uitextline2 += ( mover->CompressorFlag ? "C" : ( false == mover->CompressorAllowLocal ? "-" : ( ( mover->CompressorAllow || mover->CompressorStart == start_t::automatic ) ? "c" : "." ) ) ); + uitextline2 += ( mover->CompressorGovernorLock ? "!" : "." ); + + auto const *train { simulation::Train }; + if( ( train != nullptr ) && ( train->Dynamic() == vehicle ) ) { + uitextline2 += ( mover->Radio ? " R: " : " r: " ) + std::to_string( train->RadioChannel() ); + } + uitextline2 += " Bdelay: "; + if( ( mover->BrakeDelayFlag & bdelay_G ) == bdelay_G ) + uitextline2 += "G"; + if( ( mover->BrakeDelayFlag & bdelay_P ) == bdelay_P ) + uitextline2 += "P"; + if( ( mover->BrakeDelayFlag & bdelay_R ) == bdelay_R ) + uitextline2 += "R"; + if( ( mover->BrakeDelayFlag & bdelay_M ) == bdelay_M ) + uitextline2 += "+Mg"; + + uitextline2 += ", Load: " + to_string( mover->Load, 0 ) + " (" + to_string( mover->LoadFlag, 0 ) + ")"; + + uitextline2 += + "; Pant: " + + to_string( mover->PantPress, 2 ) + + ( mover->bPantKurek3 ? "-ZG" : "|ZG" ); + + uitextline2 += + "; Ft: " + to_string( + mover->Ft * 0.001f * ( + mover->ActiveCab ? mover->ActiveCab : + vehicle->ctOwner ? vehicle->ctOwner->Controlling()->ActiveCab : + 1 ), 1 ) + + ", Fb: " + to_string( mover->Fb * 0.001f, 1 ) + + ", Fr: " + to_string( mover->Adhesive( mover->RunningTrack.friction ), 2 ) + + ( mover->SlippingWheels ? " (!)" : "" ); + + if( vehicle->Mechanik ) { + uitextline2 += "; Ag: " + to_string( vehicle->Mechanik->fAccGravity, 2 ) + " (" + ( vehicle->Mechanik->fAccGravity > 0.01 ? "\\" : ( vehicle->Mechanik->fAccGravity < -0.01 ? "/" : "-" ) ) + ")"; + } + + uitextline2 += + "; TC:" + + to_string( mover->TotalCurrent, 0 ); + auto const frontcouplerhighvoltage = + to_string( mover->Couplers[ side::front ].power_high.voltage, 0 ) + + "@" + + to_string( mover->Couplers[ side::front ].power_high.current, 0 ); + auto const rearcouplerhighvoltage = + to_string( mover->Couplers[ side::rear ].power_high.voltage, 0 ) + + "@" + + to_string( mover->Couplers[ side::rear ].power_high.current, 0 ); + uitextline2 += ", HV: "; + if( mover->Couplers[ side::front ].power_high.local == false ) { + uitextline2 += + "(" + frontcouplerhighvoltage + ")-" + + ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:" + + "-(" + rearcouplerhighvoltage + ")"; + } + else { + uitextline2 += + frontcouplerhighvoltage + + ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:" + + rearcouplerhighvoltage; + } + + uitextline3 += + "TrB: " + to_string( mover->BrakePress, 2 ) + + ", " + to_hex_str( mover->Hamulec->GetBrakeStatus(), 2 ); + + uitextline3 += + "; LcB: " + to_string( mover->LocBrakePress, 2 ) + + "; hat: " + to_string( mover->BrakeCtrlPos2, 2 ) + + "; pipes: " + to_string( mover->PipePress, 2 ) + + "/" + to_string( mover->ScndPipePress, 2 ) + + "/" + to_string( mover->EqvtPipePress, 2 ) + + ", MT: " + to_string( mover->CompressedVolume, 3 ) + + ", BT: " + to_string( mover->Volume, 3 ) + + ", CtlP: " + to_string( mover->CntrlPipePress, 3 ) + + ", CtlT: " + to_string( mover->Hamulec->GetCRP(), 3 ); + + if( mover->ManualBrakePos > 0 ) { + + uitextline3 += "; manual brake on"; + } + + if( vehicle->Mechanik ) { + // o ile jest ktoś w środku + std::string flags = "cpapcplhhndoiefgvdpseil "; // flagi AI (definicja w Driver.h) + for( int i = 0, j = 1; i < 23; ++i, j <<= 1 ) + if( false == ( vehicle->Mechanik->DrivigFlags() & j ) ) // jak bit ustawiony + flags[ i ] = '.';// std::toupper( flags[ i ] ); // ^= 0x20; // to zmiana na wielką literę + + uitextline4 = flags; + + uitextline4 += + "Driver: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 ) + + " Ad=" + to_string( vehicle->Mechanik->AccDesired, 2 ) + + " Ah=" + to_string( vehicle->Mechanik->fAccThreshold, 2 ) + + "@" + to_string( vehicle->Mechanik->fBrake_a0[ 0 ], 2 ) + + "+" + to_string( vehicle->Mechanik->fBrake_a1[ 0 ], 2 ) + + " Bd=" + to_string( vehicle->Mechanik->fBrakeDist, 0 ) + + " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 ) + + " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 ) + + " VSl=" + to_string( vehicle->Mechanik->VelSignalLast, 0 ) + + " VLl=" + to_string( vehicle->Mechanik->VelLimitLast, 0 ) + + " VRd=" + to_string( vehicle->Mechanik->VelRoad, 0 ) + + " VRst=" + to_string( vehicle->Mechanik->VelRestricted, 0 ); + + if( ( vehicle->Mechanik->VelNext == 0.0 ) + && ( vehicle->Mechanik->eSignNext ) ) { + // jeśli ma zapamiętany event semafora, nazwa eventu semafora + uitextline4 += " (" + Bezogonkow( vehicle->Mechanik->eSignNext->asName ) + ")"; + } + + // biezaca komenda dla AI + uitextline4 += ", command: " + vehicle->Mechanik->OrderCurrent(); + } + + if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) { + // f2 screen, track scan mode + if( vehicle->Mechanik == nullptr ) { + //żeby była tabelka, musi być AI + break; + } + + std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( vehicle->Mechanik->TableSize() ) - 1, 0, 30 ); + do { + std::string scanline = vehicle->Mechanik->TableText( i ); + if( scanline.empty() ) { break; } + UITable.text_lines.emplace_back( Bezogonkow( scanline ), Global.UITextColor ); + ++i; + } while( i < speedtablesize ); // TController:iSpeedTableSize TODO: change when the table gets recoded + } + } + else { + // wyświetlenie współrzędnych w scenerii oraz kąta kamery, gdy nie mamy wskaźnika + uitextline1 = + "Camera position: " + + to_string( camera->Pos.x, 2 ) + " " + + to_string( camera->Pos.y, 2 ) + " " + + to_string( camera->Pos.z, 2 ) + + ", azimuth: " + + to_string( 180.0 - glm::degrees( camera->Yaw ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód + + " " + + std::string( "S SEE NEN NWW SW" ) + .substr( 0 + 2 * floor( fmod( 8 + ( camera->Yaw + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); + // current luminance level + uitextline2 = "Light level: " + to_string( Global.fLuminance, 3 ); + if( Global.FakeLight ) { uitextline2 += "(*)"; } + } + + break; + } + + case( GLFW_KEY_F8 ) : { + // gfx renderer data + uitextline1 = + "FoV: " + to_string( Global.FieldOfView / Global.ZoomFactor, 1 ) + + ", Draw range x " + to_string( Global.fDistanceFactor, 1 ) +// + "; sectors: " + std::to_string( GfxRenderer.m_drawcount ) +// + ", FPS: " + to_string( Timer::GetFPS(), 2 ); + + ", FPS: " + std::to_string( static_cast(std::round(GfxRenderer.Framerate())) ); + if( Global.iSlowMotion ) { + uitextline1 += " (slowmotion " + to_string( Global.iSlowMotion ) + ")"; + } + + uitextline2 = + std::string( "Rendering mode: " ) + + ( Global.bUseVBO ? + "VBO" : + "Display Lists" ) + + " "; + if( false == Global.LastGLError.empty() ) { + uitextline2 += + "Last openGL error: " + + Global.LastGLError; + } + // renderer stats + uitextline3 = GfxRenderer.info_times(); + uitextline4 = GfxRenderer.info_stats(); + + break; + } + + case( GLFW_KEY_F9 ) : { + // informacja o wersji + uitextline1 = "MaSzyna " + Global.asVersion; // informacja o wersji + if( Global.iMultiplayer ) { + uitextline1 += " (multiplayer mode is active)"; + } + uitextline3 = + "vehicles: " + to_string( Timer::subsystem.sim_dynamics.average(), 2 ) + " msec" + + " update total: " + to_string( Timer::subsystem.sim_total.average(), 2 ) + " msec"; + // current event queue + auto const time { Timer::GetTime() }; + auto const *event { simulation::Events.begin() }; + auto eventtableindex{ 0 }; + while( ( event != nullptr ) + && ( eventtableindex < 30 ) ) { + + if( ( false == event->m_ignored ) + && ( true == event->bEnabled ) ) { + + auto const delay { " " + to_string( std::max( 0.0, event->fStartTime - time ), 1 ) }; + auto const eventline = + "Delay: " + delay.substr( delay.length() - 6 ) + + ", Event: " + event->asName + + ( event->Activator ? " (by: " + event->Activator->asName + ")" : "" ) + + ( event->evJoined ? " (joint event)" : "" ); + + UITable.text_lines.emplace_back( eventline, Global.UITextColor ); + ++eventtableindex; + } + event = event->evNext; + } + + break; + } + + case( GLFW_KEY_F10 ) : { + + uitextline1 = "Press [Y] key to quit / Aby zakonczyc program, przycisnij klawisz [Y]."; + + break; + } + + case( GLFW_KEY_F12 ) : { + // opcje włączenia i wyłączenia logowania + uitextline1 = "[0] Debugmode " + std::string( DebugModeFlag ? "(on)" : "(off)" ); + uitextline2 = "[1] log.txt " + std::string( ( Global.iWriteLogEnabled & 1 ) ? "(on)" : "(off)" ); + uitextline3 = "[2] Console " + std::string( ( Global.iWriteLogEnabled & 2 ) ? "(on)" : "(off)" ); + + break; + } + + default: { + // uncovered cases, nothing to do here... + // ... unless we're in debug mode + if( DebugModeFlag ) { + + auto const *vehicle { + ( FreeFlyModeFlag ? + std::get( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) : + controlled ) }; // w trybie latania lokalizujemy wg mapy + if( vehicle == nullptr ) { + break; + } + auto const *mover { vehicle->MoverParameters }; + uitextline1 = + "vel: " + to_string( vehicle->GetVelocity(), 2 ) + "/" + to_string( mover->nrot* M_PI * mover->WheelDiameter * 3.6, 2 ) + + " km/h;" + ( mover->SlippingWheels ? " (!)" : " " ) + + " dist: " + to_string( mover->DistCounter, 2 ) + " km" + + "; pos: [" + to_string( vehicle->GetPosition().x, 2 ) + ", " + to_string( vehicle->GetPosition().y, 2 ) + ", " + to_string( vehicle->GetPosition().z, 2 ) + "]" + + ", PM=" + to_string( mover->WheelFlat, 1 ) + + " mm; enpwr=" + to_string( mover->EnginePower, 1 ) + + "; enrot=" + to_string( mover->enrot * 60, 0 ) + + " tmrot=" + to_string( std::abs( mover->nrot ) * mover->Transmision.Ratio * 60, 0 ) + + "; ventrot=" + to_string( mover->RventRot * 60, 1 ) + + "; fanrot=" + to_string( mover->dizel_heat.rpmw, 1 ) + ", " + to_string( mover->dizel_heat.rpmw2, 1 ); + + uitextline2 = + "HamZ=" + to_string( mover->fBrakeCtrlPos, 2 ) + + "; HamP=" + to_string( mover->LocalBrakePosA, 2 ) + + "; NasJ=" + std::to_string( mover->MainCtrlPos ) + "(" + std::to_string( mover->MainCtrlActualPos ) + ")" + + ( ( mover->ShuntMode && mover->EngineType == TEngineType::DieselElectric ) ? + "; NasB=" + to_string( mover->AnPos, 2 ) : + "; NasB=" + std::to_string( mover->ScndCtrlPos ) + "(" + std::to_string( mover->ScndCtrlActualPos ) + ")" ) + + "; I=" + + ( mover->TrainType == dt_EZT ? + std::to_string( int( mover->ShowCurrent( 0 ) ) ) : + std::to_string( int( mover->Im ) ) ) + + "; U=" + to_string( int( mover->RunningTraction.TractionVoltage + 0.5 ) ) + + "; R=" + + ( std::abs( mover->RunningShape.R ) > 10000.0 ? + "~0.0" : + to_string( mover->RunningShape.R, 1 ) ) + + " An=" + to_string( mover->AccN, 2 ); // przyspieszenie poprzeczne + + if( tprev != simulation::Time.data().wSecond ) { + tprev = simulation::Time.data().wSecond; + Acc = ( mover->Vel - VelPrev ) / 3.6; + VelPrev = mover->Vel; + } + uitextline2 += "; As=" + to_string( Acc, 2 ); // przyspieszenie wzdłużne +// uitextline2 += " eAngle=" + to_string( std::cos( mover->eAngle ), 2 ); + uitextline2 += "; oilP=" + to_string( mover->OilPump.pressure_present, 3 ); + uitextline2 += " oilT=" + to_string( mover->dizel_heat.To, 2 ); + uitextline2 += "; waterT=" + to_string( mover->dizel_heat.temperatura1, 2 ); + uitextline2 += ( mover->WaterCircuitsLink ? "-" : "|" ); + uitextline2 += to_string( mover->dizel_heat.temperatura2, 2 ); + uitextline2 += "; engineT=" + to_string( mover->dizel_heat.Ts, 2 ); + + uitextline3 = + "cyl.ham. " + to_string( mover->BrakePress, 2 ) + + "; prz.gl. " + to_string( mover->PipePress, 2 ) + + "; zb.gl. " + to_string( mover->CompressedVolume, 2 ) + // youBy - drugi wezyk + + "; p.zas. " + to_string( mover->ScndPipePress, 2 ); + + // McZapkie: warto wiedziec w jakim stanie sa przelaczniki + if( mover->ConvOvldFlag ) + uitextline3 += " C! "; + else if( mover->FuseFlag ) + uitextline3 += " F! "; + else if( !mover->Mains ) + uitextline3 += " () "; + else { + switch( + mover->ActiveDir * + ( mover->Imin == mover->IminLo ? + 1 : + 2 ) ) { + case 2: { uitextline3 += " >> "; break; } + case 1: { uitextline3 += " -> "; break; } + case 0: { uitextline3 += " -- "; break; } + case -1: { uitextline3 += " <- "; break; } + case -2: { uitextline3 += " << "; break; } + } + } + // McZapkie: predkosc szlakowa + if( mover->RunningTrack.Velmax == -1 ) { + uitextline3 += " Vtrack=Vmax"; + } + else { + uitextline3 += " Vtrack " + to_string( mover->RunningTrack.Velmax, 2 ); + } + + if( ( mover->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) + || ( mover->TrainType == dt_EZT ) ) { + uitextline3 += + "; pant. " + to_string( mover->PantPress, 2 ) + + ( mover->bPantKurek3 ? "=" : "^" ) + "ZG"; + } + + // McZapkie: komenda i jej parametry + if( mover->CommandIn.Command != ( "" ) ) { + uitextline4 = + "C:" + mover->CommandIn.Command + + " V1=" + to_string( mover->CommandIn.Value1, 0 ) + + " V2=" + to_string( mover->CommandIn.Value2, 0 ); + } + if( ( vehicle->Mechanik ) + && ( vehicle->Mechanik->AIControllFlag == AIdriver ) ) { + uitextline4 += + "AI: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 ) + + " ad=" + to_string(vehicle->Mechanik->AccDesired, 2) + + "/" + to_string(vehicle->Mechanik->AccDesired*vehicle->Mechanik->BrakeAccFactor(), 2) + + " atrain=" + to_string(vehicle->Mechanik->fBrake_a0[0], 2) + + "+" + to_string(vehicle->Mechanik->fBrake_a1[0], 2) + + " aS=" + to_string(vehicle->Mechanik->AbsAccS_pub, 2) + + " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 ) + + " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 ); + } + + // induction motor data + if( mover->EngineType == TEngineType::ElectricInductionMotor ) { + + UITable.text_lines.emplace_back( " eimc: eimv: press:", Global.UITextColor ); + for( int i = 0; i <= 20; ++i ) { + + std::string parameters = + mover->eimc_labels[ i ] + to_string( mover->eimc[ i ], 2, 9 ) + + " | " + + mover->eimv_labels[ i ] + to_string( mover->eimv[ i ], 2, 9 ); + + if( i < 10 ) { + parameters += " | " + train->fPress_labels[i] + to_string( train->fPress[ i ][ 0 ], 2, 9 ); + } + else if( i == 12 ) { + parameters += " med:"; + } + else if( i >= 13 ) { + parameters += " | " + vehicle->MED_labels[ i - 13 ] + to_string( vehicle->MED[ 0 ][ i - 13 ], 2, 9 ); + } + + UITable.text_lines.emplace_back( parameters, Global.UITextColor ); + } + } + if (mover->EngineType == TEngineType::DieselEngine) { + std::string parameters = "param value"; + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "efill: " + to_string(mover->dizel_fill, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "etorq: " + to_string(mover->dizel_Torque, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "creal: " + to_string(mover->dizel_engage, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "cdesi: " + to_string(mover->dizel_engagestate, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "cdelt: " + to_string(mover->dizel_engagedeltaomega, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "gears: " + to_string(mover->dizel_automaticgearstatus, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hydro value"; + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCnI: " + to_string(mover->hydro_TC_nIn, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCnO: " + to_string(mover->hydro_TC_nOut, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCTM: " + to_string(mover->hydro_TC_TMRatio, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCTI: " + to_string(mover->hydro_TC_TorqueIn, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCTO: " + to_string(mover->hydro_TC_TorqueOut, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCfl: " + to_string(mover->hydro_TC_Fill, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + parameters = "hTCLR: " + to_string(mover->hydro_TC_LockupRate, 2, 9); + UITable.text_lines.emplace_back(parameters, Global.UITextColor); + //parameters = "hTCXX: " + to_string(mover->hydro_TC_nIn, 2, 9); + //UITable.text_lines.emplace_back(parameters, Global.UITextColor); + } + + } // if( DebugModeFlag && Controlled ) + + break; + } + } + +#ifdef EU07_USE_OLD_UI_CODE + if( Controlled && DebugModeFlag && !Global.iTextMode ) { + + uitextline1 += + ( "; d_omega " ) + to_string( Controlled->MoverParameters->dizel_engagedeltaomega, 3 ); + + if( Controlled->MoverParameters->EngineType == ElectricInductionMotor ) { + + for( int i = 0; i <= 8; i++ ) { + for( int j = 0; j <= 9; j++ ) { + glRasterPos2f( 0.05f + 0.03f * i, 0.16f - 0.01f * j ); + uitextline4 = to_string( Train->fEIMParams[ i ][ j ], 2 ); + } + } + } + } +#endif + + // update the ui header texts + auto &headerdata = UIHeader.text_lines; + headerdata[ 0 ].data = uitextline1; + headerdata[ 1 ].data = uitextline2; + headerdata[ 2 ].data = uitextline3; + headerdata[ 3 ].data = uitextline4; + + // stenogramy dźwięków (ukryć, gdy tabelka skanowania lub rozkład?) + auto &transcripts = UITranscripts.text_lines; + transcripts.clear(); + for( auto const &transcript : ui::Transcripts.aLines ) { + + if( Global.fTimeAngleDeg >= transcript.fShow ) { + + cParser parser( transcript.asText ); + while( true == parser.getTokens( 1, false, "|" ) ) { + + std::string transcriptline; parser >> transcriptline; + transcripts.emplace_back( transcriptline, glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f ) ); + } + } + } + +} diff --git a/driveruilayer.h b/driveruilayer.h new file mode 100644 index 00000000..6d22e578 --- /dev/null +++ b/driveruilayer.h @@ -0,0 +1,36 @@ +/* +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 + +#include "uilayer.h" + +class driver_ui : public ui_layer { + +public: +// constructors + driver_ui(); +// methods + // potentially processes provided input key. returns: true if the input was processed, false otherwise + bool + on_key( int const Key, int const Action ) override; + // updates state of UI elements + void + update() override; + +private: +// members + ui_panel UIHeader { 20, 20 }; // header ui panel + ui_panel UITable { 20, 100 };// schedule or scan table + ui_panel UITranscripts { 85, 600 }; // voice transcripts + int tprev { 0 }; // poprzedni czas + double VelPrev { 0.0 }; // poprzednia prędkość + double Acc { 0.0 }; // przyspieszenie styczne + +}; diff --git a/dumb3d.h b/dumb3d.h index ceec7c21..8a39af53 100644 --- a/dumb3d.h +++ b/dumb3d.h @@ -68,12 +68,13 @@ class vector3 vector3( scalar_t X, scalar_t Y, scalar_t Z ) : x( X ), y( Y ), z( Z ) {} - vector3( glm::dvec3 const &Vector ) : + template + vector3( glm::tvec3 const &Vector ) : x( Vector.x ), y( Vector.y ), z( Vector.z ) {} - template - operator glm::tvec3() const { - return glm::tvec3{ x, y, z }; } + template + operator glm::tvec3() const { + return glm::tvec3{ x, y, z }; } // The int parameter is the number of elements to copy from initArray (3 or 4) // explicit vector3(scalar_t* initArray, int arraySize = 3) // { for (int i = 0;i(); +} + +// initializes internal data structures of the mode. returns: true on success, false otherwise +bool +editor_mode::init() { + + Camera.Init( {}, {}, TCameraType::tp_Free ); + + return m_input.init(); +} + +// mode-specific update of simulation data. returns: false on error, true otherwise +bool +editor_mode::update() { + + Timer::UpdateTimers( true ); + + simulation::State.update_clocks(); + simulation::Environment.update(); + + // render time routines follow: + auto const deltarealtime = Timer::GetDeltaRenderTime(); // nie uwzględnia pauzowania ani mnożenia czasu + + // fixed step render time routines: + fTime50Hz += deltarealtime; // w pauzie też trzeba zliczać czas, bo przy dużym FPS będzie problem z odczytem ramek + while( fTime50Hz >= 1.0 / 50.0 ) { + Console::Update(); // to i tak trzeba wywoływać + m_userinterface->update(); + // decelerate camera + Camera.Velocity *= 0.65; + if( std::abs( Camera.Velocity.x ) < 0.01 ) { Camera.Velocity.x = 0.0; } + if( std::abs( Camera.Velocity.y ) < 0.01 ) { Camera.Velocity.y = 0.0; } + if( std::abs( Camera.Velocity.z ) < 0.01 ) { Camera.Velocity.z = 0.0; } + + fTime50Hz -= 1.0 / 50.0; + } + + // variable step render time routines: + update_camera( deltarealtime ); + + simulation::Region->update_sounds(); + audio::renderer.update( deltarealtime ); + + GfxRenderer.Update( deltarealtime ); + + simulation::is_ready = true; + + m_input.poll(); + + return true; +} + +void +editor_mode::update_camera( double const Deltatime ) { + + // uwzględnienie ruchu wywołanego klawiszami + Camera.Update(); + // reset window state, it'll be set again if applicable in a check below + Global.CabWindowOpen = false; + // all done, update camera position to the new value + Global.pCameraPosition = Camera.Pos; +} + +// maintenance method, called when the mode is activated +void +editor_mode::enter() { + + m_statebackup = { Global.pCamera, FreeFlyModeFlag, Global.ControlPicking }; + + Global.pCamera = &Camera; + FreeFlyModeFlag = true; + Global.ControlPicking = true; + EditorModeFlag = true; + + Application.set_cursor( GLFW_CURSOR_NORMAL ); +} + +// maintenance method, called when the mode is deactivated +void +editor_mode::exit() { + + EditorModeFlag = false; + Global.ControlPicking = m_statebackup.picking; + FreeFlyModeFlag = m_statebackup.freefly; + Global.pCamera = m_statebackup.camera; + + Application.set_cursor( + ( Global.ControlPicking ? + GLFW_CURSOR_NORMAL : + GLFW_CURSOR_DISABLED ) ); +} + +void +editor_mode::on_key( int const Key, int const Scancode, int const Action, int const 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 == m_userinterface->on_key( Key, Action ) ) { return; } + // ...if the input is left untouched, pass it on + if( true == m_input.keyboard.key( Key, Action ) ) { return; } + + if( Action == GLFW_RELEASE ) { return; } + + 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; + } + + // legacy hardcoded keyboard commands + // TODO: replace with current command system, move to input object(s) + switch( Key ) { + + case GLFW_KEY_F11: { + + if( Action != GLFW_PRESS ) { break; } + // mode switch + // TODO: unsaved changes warning + if( ( false == Global.ctrlState ) + && ( false == Global.shiftState ) ) { + Application.pop_mode(); + } + // scenery export + if( Global.ctrlState + && Global.shiftState ) { + simulation::State.export_as_text( Global.SceneryFile ); + } + break; + } + + case GLFW_KEY_F12: { + // quick debug mode toggle + if( Global.ctrlState + && Global.shiftState ) { + DebugModeFlag = !DebugModeFlag; + } + break; + } + + default: { + break; + } + } +} + +void +editor_mode::on_cursor_pos( double const Horizontal, double const Vertical ) { + + auto const mousemove { glm::dvec2{ Horizontal, Vertical } - m_input.mouse.position() }; + m_input.mouse.position( Horizontal, Vertical ); + + if( m_input.mouse.button( GLFW_MOUSE_BUTTON_LEFT ) == GLFW_RELEASE ) { return; } + if( m_node == nullptr ) { return; } + + if( m_takesnapshot ) { + // take a snapshot of selected node(s) + // TODO: implement this + m_takesnapshot = false; + } + + if( mode_translation() ) { + // move selected node + if( mode_translation_vertical() ) { + auto const translation { mousemove.y * -0.01f }; + m_editor.translate( m_node, translation ); + } + else { + auto const mouseworldposition { Camera.Pos + GfxRenderer.Mouse_Position() }; + m_editor.translate( m_node, mouseworldposition, mode_snap() ); + } + } + else { + // rotate selected node + auto const rotation { glm::vec3 { mousemove.y, mousemove.x, 0 } * 0.25f }; + auto const quantization { ( + mode_snap() ? + 15.f : // TODO: put quantization value in a variable + 0.f ) }; + m_editor.rotate( m_node, rotation, quantization ); + } + +} + +void +editor_mode::on_mouse_button( int const Button, int const Action, int const Mods ) { + + if( Button == GLFW_MOUSE_BUTTON_LEFT ) { + + if( Action == GLFW_PRESS ) { + // left button press + m_node = GfxRenderer.Update_Pick_Node(); + if( m_node ) { + Application.set_cursor( GLFW_CURSOR_DISABLED ); + } + dynamic_cast( m_userinterface.get() )->set_node( m_node ); + } + else { + // left button release + if( m_node ) { + Application.set_cursor( GLFW_CURSOR_NORMAL ); + } + // prime history stack for another snapshot + m_takesnapshot = true; + } + } + + m_input.mouse.button( Button, Action ); +} + +bool +editor_mode::mode_translation() const { + + return ( false == Global.altState ); +} + +bool +editor_mode::mode_translation_vertical() const { + + return ( true == Global.shiftState ); +} + +bool +editor_mode::mode_rotation() const { + + return ( true == Global.altState ); +} + +bool +editor_mode::mode_snap() const { + + return ( true == Global.ctrlState ); +} diff --git a/editormode.h b/editormode.h new file mode 100644 index 00000000..012752ff --- /dev/null +++ b/editormode.h @@ -0,0 +1,84 @@ +/* +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 + +#include "applicationmode.h" +#include "editormouseinput.h" +#include "editorkeyboardinput.h" +#include "camera.h" +#include "sceneeditor.h" +#include "scenenode.h" + +class editor_mode : public application_mode { + +public: +// constructors + editor_mode(); +// methods + // initializes internal data structures of the mode. returns: true on success, false otherwise + bool + init() override; + // mode-specific update of simulation data. returns: false on error, true otherwise + bool + update() override; + // maintenance method, called when the mode is activated + void + enter() override; + // maintenance method, called when the mode is deactivated + void + exit() override; + // input handlers + void + on_key( int const Key, int const Scancode, int const Action, int const Mods ) override; + void + on_cursor_pos( double const Horizontal, double const Vertical ) override; + void + on_mouse_button( int const Button, int const Action, int const Mods ) override; + void + on_scroll( double const Xoffset, double const Yoffset ) override { ; } + +private: +// types + struct editormode_input { + + editormouse_input mouse; + editorkeyboard_input keyboard; + + bool init(); + void poll(); + }; + + struct state_backup { + + TCamera *camera; + bool freefly; + bool picking; + }; +// methods + void + update_camera( double const Deltatime ); + bool + mode_translation() const; + bool + mode_translation_vertical() const; + bool + mode_rotation() const; + bool + mode_snap() const; +// members + editormode_input m_input; + TCamera Camera; + double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys + scene::basic_editor m_editor; + scene::basic_node *m_node; // currently selected scene node + bool m_takesnapshot { true }; // helper, hints whether snapshot of selected node(s) should be taken before modification + state_backup m_statebackup; // helper, cached variables to be restored on mode exit + +}; diff --git a/editormouseinput.cpp b/editormouseinput.cpp new file mode 100644 index 00000000..a8a01ad2 --- /dev/null +++ b/editormouseinput.cpp @@ -0,0 +1,55 @@ +/* +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 "editormouseinput.h" + +bool +editormouse_input::init() { + + return true; +} + +void +editormouse_input::position( double Horizontal, double Vertical ) { + + if( false == m_pickmodepanning ) { + // even if the view panning isn't active we capture the cursor position in case it does get activated + m_cursorposition.x = Horizontal; + m_cursorposition.y = Vertical; + return; + } + glm::dvec2 cursorposition { Horizontal, Vertical }; + auto const viewoffset = cursorposition - m_cursorposition; + m_relay.post( + user_command::viewturn, + viewoffset.x, + viewoffset.y, + GLFW_PRESS, + // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 + // TODO: pass correct entity id once the missing systems are in place + 0 ); + m_cursorposition = cursorposition; +} + +void +editormouse_input::button( int const Button, int const Action ) { + + // store key state + if( Button >= 0 ) { + m_buttons[ Button ] = Action; + } + + // right button controls panning + if( Button == GLFW_MOUSE_BUTTON_RIGHT ) { + m_pickmodepanning = ( Action == GLFW_PRESS ); + } +} + +//--------------------------------------------------------------------------- diff --git a/editormouseinput.h b/editormouseinput.h new file mode 100644 index 00000000..3f33af2c --- /dev/null +++ b/editormouseinput.h @@ -0,0 +1,44 @@ +/* +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 + +#include "command.h" + +class editormouse_input { + +public: +// constructors + editormouse_input() = default; + +// methods + bool + init(); + void + position( double const Horizontal, double const Vertical ); + inline + glm::dvec2 + position() const { + return m_cursorposition; } + void + button( int const Button, int const Action ); + inline + int + button( int const Button ) const { + return m_buttons[ Button ]; } + +private: +// members + command_relay m_relay; + bool m_pickmodepanning { false }; // indicates mouse is in view panning mode + glm::dvec2 m_cursorposition { 0.0 }; // stored last cursor position, used for panning + std::array m_buttons { GLFW_RELEASE }; +}; + +//--------------------------------------------------------------------------- diff --git a/editoruilayer.cpp b/editoruilayer.cpp new file mode 100644 index 00000000..be96c79b --- /dev/null +++ b/editoruilayer.cpp @@ -0,0 +1,205 @@ +/* +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 "editoruilayer.h" + +#include "globals.h" +#include "camera.h" +#include "animmodel.h" +#include "track.h" +#include "event.h" +#include "renderer.h" +#include "utilities.h" +#include "logs.h" + +editor_ui::editor_ui() { + + clear_texts(); +/* + UIHeader = std::make_shared( 20, 20 ); // header ui panel +*/ + // make 4 empty lines for the ui header, to cut down on work down the road + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + UIHeader.text_lines.emplace_back( "", Global.UITextColor ); + // bind the panels with ui object. maybe not the best place for this but, eh + push_back( &UIHeader ); +} + +// potentially processes provided input key. returns: true if key was processed, false otherwise +bool +editor_ui::on_key( int const Key, int const Action ) { + + return false; +} + +// updates state of UI elements +void +editor_ui::update() { + + std::string uitextline1, uitextline2, uitextline3, uitextline4; + set_tooltip( "" ); + + auto const *camera { Global.pCamera }; + + if( ( true == Global.ControlPicking ) + && ( true == DebugModeFlag ) ) { + + auto const scenerynode = GfxRenderer.Pick_Node(); + set_tooltip( + ( scenerynode ? + scenerynode->name() : + "" ) ); + } + + + // scenario inspector + auto const *node { m_node }; + + if( node == nullptr ) { + auto const mouseposition { Global.pCamera->Pos + GfxRenderer.Mouse_Position() }; + uitextline1 = "mouse location: [" + to_string( mouseposition.x, 2 ) + ", " + to_string( mouseposition.y, 2 ) + ", " + to_string( mouseposition.z, 2 ) + "]"; + goto update; + } + + uitextline1 = + "node name: " + node->name() + + "; location: [" + to_string( node->location().x, 2 ) + ", " + to_string( node->location().y, 2 ) + ", " + to_string( node->location().z, 2 ) + "]" + + " (distance: " + to_string( glm::length( glm::dvec3{ node->location().x, 0.0, node->location().z } -glm::dvec3{ Global.pCameraPosition.x, 0.0, Global.pCameraPosition.z } ), 1 ) + " m)"; + // subclass-specific data + // TBD, TODO: specialized data dump method in each node subclass, or data imports in the panel for provided subclass pointer? + if( typeid( *node ) == typeid( TAnimModel ) ) { + + auto const *subnode = static_cast( node ); + + uitextline2 = "angle: " + to_string( clamp_circular( subnode->vAngle.y, 360.f ), 2 ) + " deg"; + uitextline2 += "; lights: "; + if( subnode->iNumLights > 0 ) { + uitextline2 += '['; + for( int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx ) { + uitextline2 += to_string( subnode->lsLights[ lightidx ] ); + if( lightidx < subnode->iNumLights - 1 ) { + uitextline2 += ", "; + } + } + uitextline2 += ']'; + } + else { + uitextline2 += "none"; + } + // 3d shape + auto modelfile { ( + ( subnode->pModel != nullptr ) ? + subnode->pModel->NameGet() : + "none" ) }; + if( modelfile.find( szModelPath ) == 0 ) { + // don't include 'models/' in the path + modelfile.erase( 0, std::string{ szModelPath }.size() ); + } + // texture + auto texturefile { ( + ( subnode->Material()->replacable_skins[ 1 ] != null_handle ) ? + GfxRenderer.Material( subnode->Material()->replacable_skins[ 1 ] ).name : + "none" ) }; + if( texturefile.find( szTexturePath ) == 0 ) { + // don't include 'textures/' in the path + texturefile.erase( 0, std::string{ szTexturePath }.size() ); + } + uitextline3 = "mesh: " + modelfile; + uitextline4 = "skin: " + texturefile; + } + else if( typeid( *node ) == typeid( TTrack ) ) { + + auto const *subnode = static_cast( node ); + // basic attributes + uitextline2 = + "isolated: " + ( ( subnode->pIsolated != nullptr ) ? subnode->pIsolated->asName : "none" ) + + "; velocity: " + to_string( subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity ) + + "; width: " + to_string( subnode->fTrackWidth ) + " m" + + "; friction: " + to_string( subnode->fFriction, 2 ) + + "; quality: " + to_string( subnode->iQualityFlag ); + // textures + auto texturefile { ( + ( subnode->m_material1 != null_handle ) ? + GfxRenderer.Material( subnode->m_material1 ).name : + "none" ) }; + if( texturefile.find( szTexturePath ) == 0 ) { + texturefile.erase( 0, std::string{ szTexturePath }.size() ); + } + auto texturefile2{ ( + ( subnode->m_material2 != null_handle ) ? + GfxRenderer.Material( subnode->m_material2 ).name : + "none" ) }; + if( texturefile2.find( szTexturePath ) == 0 ) { + texturefile2.erase( 0, std::string{ szTexturePath }.size() ); + } + uitextline2 += "; skins: [" + texturefile + ", " + texturefile2 + "]"; + // paths + uitextline3 = "paths: "; + for( auto const &path : subnode->m_paths ) { + uitextline3 += + "[" + + to_string( path.points[ segment_data::point::start ].x, 3 ) + ", " + + to_string( path.points[ segment_data::point::start ].y, 3 ) + ", " + + to_string( path.points[ segment_data::point::start ].z, 3 ) + "]->" + + "[" + + to_string( path.points[ segment_data::point::end ].x, 3 ) + ", " + + to_string( path.points[ segment_data::point::end ].y, 3 ) + ", " + + to_string( path.points[ segment_data::point::end ].z, 3 ) + "] "; + } + // events + std::vector< std::pair< std::string, TTrack::event_sequence const * > > const eventsequences { + { "ev0", &subnode->m_events0 }, { "ev0all", &subnode->m_events0all }, + { "ev1", &subnode->m_events1 }, { "ev1all", &subnode->m_events1all }, + { "ev2", &subnode->m_events2 }, { "ev2all", &subnode->m_events2all } }; + + for( auto const &eventsequence : eventsequences ) { + + if( eventsequence.second->empty() ) { continue; } + + uitextline4 += eventsequence.first + ": ["; + for( auto const &event : *( eventsequence.second ) ) { + if( uitextline4.back() != '[' ) { + uitextline4 += ", "; + } + if( event.second ) { + uitextline4 += event.second->asName; + } + } + uitextline4 += "] "; + } + + } + else if( typeid( *node ) == typeid( TMemCell ) ) { + + auto const *subnode = static_cast( node ); + + uitextline2 = + "data: [" + subnode->Text() + "]" + + " [" + to_string( subnode->Value1(), 2 ) + "]" + + " [" + to_string( subnode->Value2(), 2 ) + "]"; + uitextline3 = "track: " + ( subnode->asTrackName.empty() ? "none" : subnode->asTrackName ); + } + +update: + // update the ui header texts + auto &headerdata = UIHeader.text_lines; + headerdata[ 0 ].data = uitextline1; + headerdata[ 1 ].data = uitextline2; + headerdata[ 2 ].data = uitextline3; + headerdata[ 3 ].data = uitextline4; +} + +void +editor_ui::set_node( scene::basic_node * Node ) { + + m_node = Node; +} diff --git a/editoruilayer.h b/editoruilayer.h new file mode 100644 index 00000000..f0bafc87 --- /dev/null +++ b/editoruilayer.h @@ -0,0 +1,39 @@ +/* +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 + +#include "uilayer.h" + +namespace scene { + +class basic_node; + +} + +class editor_ui : public ui_layer { + +public: +// constructors + editor_ui(); +// methods + // potentially processes provided input key. returns: true if the input was processed, false otherwise + bool + on_key( int const Key, int const Action ) override; + // updates state of UI elements + void + update() override; + void + set_node( scene::basic_node * Node ); + +private: +// members + ui_panel UIHeader { 20, 20 }; // header ui panel + scene::basic_node * m_node { nullptr }; // currently bound scene node, if any +}; diff --git a/keyboardinput.cpp b/keyboardinput.cpp index b68326f8..1c88d955 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -13,6 +13,15 @@ http://mozilla.org/MPL/2.0/. #include "logs.h" #include "parser.h" +namespace input { + +std::array keys { GLFW_RELEASE }; +bool key_alt; +bool key_ctrl; +bool key_shift; + +} + bool keyboard_input::recall_bindings() { @@ -94,7 +103,7 @@ keyboard_input::recall_bindings() { } if( ( binding & 0xffff ) != 0 ) { - m_commands.at( static_cast( lookup->second ) ).binding = binding; + m_bindingsetups.emplace_back( binding_setup{ lookup->second, binding } ); } } } @@ -113,7 +122,7 @@ keyboard_input::key( int const Key, int const Action ) { if( ( Key == GLFW_KEY_LEFT_SHIFT ) || ( Key == GLFW_KEY_RIGHT_SHIFT ) ) { // update internal state, but don't bother passing these - m_shift = + input::key_shift = ( Action == GLFW_RELEASE ? false : true ); @@ -122,20 +131,23 @@ keyboard_input::key( int const Key, int const Action ) { } if( ( Key == GLFW_KEY_LEFT_CONTROL ) || ( Key == GLFW_KEY_RIGHT_CONTROL ) ) { // update internal state, but don't bother passing these - m_ctrl = + input::key_ctrl = ( Action == GLFW_RELEASE ? false : true ); modifier = true; } if( ( Key == GLFW_KEY_LEFT_ALT ) || ( Key == GLFW_KEY_RIGHT_ALT ) ) { - // currently we have no interest in these whatsoever - return false; + // update internal state, but don't bother passing these + input::key_alt = + ( Action == GLFW_RELEASE ? + false : + true ); } // store key state if( Key != -1 ) { - m_keys[ Key ] = Action; + input::keys[ Key ] = Action; } if( true == is_movement_key( Key ) ) { @@ -146,8 +158,8 @@ keyboard_input::key( int const Key, int const Action ) { // include active modifiers for currently pressed key, except if the key is a modifier itself auto const key = Key - | ( modifier ? 0 : ( m_shift ? keymodifier::shift : 0 ) ) - | ( modifier ? 0 : ( m_ctrl ? keymodifier::control : 0 ) ); + | ( modifier ? 0 : ( input::key_shift ? keymodifier::shift : 0 ) ) + | ( modifier ? 0 : ( input::key_ctrl ? keymodifier::control : 0 ) ); auto const lookup = m_bindings.find( key ); if( lookup == m_bindings.end() ) { @@ -166,384 +178,10 @@ keyboard_input::key( int const Key, int const Action ) { return true; } -void -keyboard_input::default_bindings() { - - m_commands = { - // aidriverenable - { GLFW_KEY_Q | keymodifier::shift }, - // aidriverdisable - { GLFW_KEY_Q }, - // mastercontrollerincrease - { GLFW_KEY_KP_ADD }, - // mastercontrollerincreasefast - { GLFW_KEY_KP_ADD | keymodifier::shift }, - // mastercontrollerdecrease - { GLFW_KEY_KP_SUBTRACT }, - // mastercontrollerdecreasefast - { GLFW_KEY_KP_SUBTRACT | keymodifier::shift }, - // mastercontrollerset - { -1 }, - // secondcontrollerincrease - { GLFW_KEY_KP_DIVIDE }, - // secondcontrollerincreasefast - { GLFW_KEY_KP_DIVIDE | keymodifier::shift }, - // secondcontrollerdecrease - { GLFW_KEY_KP_MULTIPLY }, - // secondcontrollerdecreasefast - { GLFW_KEY_KP_MULTIPLY | keymodifier::shift }, - // secondcontrollerset - { -1 }, - // mucurrentindicatorothersourceactivate - { GLFW_KEY_Z | keymodifier::shift }, - // independentbrakeincrease - { GLFW_KEY_KP_1 }, - // independentbrakeincreasefast - { GLFW_KEY_KP_1 | keymodifier::shift }, - // independentbrakedecrease - { GLFW_KEY_KP_7 }, - // independentbrakedecreasefast - { GLFW_KEY_KP_7 | keymodifier::shift }, - // independentbrakeset - { -1 }, - // independentbrakebailoff - { GLFW_KEY_KP_4 }, - // trainbrakeincrease - { GLFW_KEY_KP_3 }, - // trainbrakedecrease - { GLFW_KEY_KP_9 }, - // trainbrakeset - { -1 }, - // trainbrakecharging - { GLFW_KEY_KP_DECIMAL }, - // trainbrakerelease - { GLFW_KEY_KP_6 }, - // trainbrakefirstservice - { GLFW_KEY_KP_8 }, - // trainbrakeservice - { GLFW_KEY_KP_5 }, - // trainbrakefullservice - { GLFW_KEY_KP_2 }, - // trainbrakehandleoff - { GLFW_KEY_KP_5 | keymodifier::control }, - // trainbrakeemergency - { GLFW_KEY_KP_0 }, - // trainbrakebasepressureincrease - { GLFW_KEY_KP_3 | keymodifier::control }, - // trainbrakebasepressuredecrease - { GLFW_KEY_KP_9 | keymodifier::control }, - // trainbrakebasepressurereset - { GLFW_KEY_KP_6 | keymodifier::control }, - // trainbrakeoperationtoggle - { GLFW_KEY_KP_4 | keymodifier::control }, - // manualbrakeincrease - { GLFW_KEY_KP_1 | keymodifier::control }, - // manualbrakedecrease - { GLFW_KEY_KP_7 | keymodifier::control }, - // alarm chain toggle - { GLFW_KEY_B | keymodifier::shift | keymodifier::control }, - // wheelspinbrakeactivate - { GLFW_KEY_KP_ENTER }, - // sandboxactivate - { GLFW_KEY_S }, - // reverserincrease - { GLFW_KEY_D }, - // reverserdecrease - { GLFW_KEY_R }, - // reverserforwardhigh - { -1 }, - // reverserforward - { -1 }, - // reverserneutral - { -1 }, - // reverserbackward - { -1 }, - // waterpumpbreakertoggle - { GLFW_KEY_W | keymodifier::control }, - // waterpumpbreakerclose - { -1 }, - // waterpumpbreakeropen - { -1 }, - // waterpumptoggle - { GLFW_KEY_W }, - // waterpumpenable - { -1 }, - // waterpumpdisable - { -1 }, - // waterheaterbreakertoggle - { GLFW_KEY_W | keymodifier::control | keymodifier::shift }, - // waterheaterbreakerclose - { -1 }, - // waterheaterbreakeropen - { -1 }, - // waterheatertoggle - { GLFW_KEY_W | keymodifier::shift }, - // waterheaterenable - { -1 }, - // waterheaterdisable - { -1 }, - // watercircuitslinktoggle - { GLFW_KEY_H | keymodifier::shift }, - // watercircuitslinkenable - { -1 }, - // watercircuitslinkdisable - { -1 }, - // fuelpumptoggle - { GLFW_KEY_F }, - // fuelpumpenable, - { -1 }, - // fuelpumpdisable, - { -1 }, - // oilpumptoggle - { GLFW_KEY_F | keymodifier::shift }, - // oilpumpenable, - { -1 }, - // oilpumpdisable, - { -1 }, - // linebreakertoggle - { GLFW_KEY_M }, - // linebreakeropen - { -1 }, - // linebreakerclose - { -1 }, - // convertertoggle - { GLFW_KEY_X }, - // converterenable, - { -1 }, - // converterdisable, - { -1 }, - // convertertogglelocal - { GLFW_KEY_X | keymodifier::shift }, - // converteroverloadrelayreset - { GLFW_KEY_N | keymodifier::control }, - // compressortoggle - { GLFW_KEY_C }, - // compressorenable - { -1 }, - // compressordisable - { -1 }, - // compressortoggleloal - { GLFW_KEY_C | keymodifier::shift }, - // motoroverloadrelaythresholdtoggle - { GLFW_KEY_F }, - // motoroverloadrelaythresholdsetlow - { -1 }, - // motoroverloadrelaythresholdsethigh - { -1 }, - // motoroverloadrelayreset - { GLFW_KEY_N }, - // notchingrelaytoggle - { GLFW_KEY_G }, - // epbrakecontroltoggle - { GLFW_KEY_Z | keymodifier::control }, - // trainbrakeoperationmodeincrease - { GLFW_KEY_KP_2 | keymodifier::control }, - // trainbrakeoperationmodedecrease - { GLFW_KEY_KP_8 | keymodifier::control }, - // brakeactingspeedincrease - { GLFW_KEY_B | keymodifier::shift }, - // brakeactingspeeddecrease - { GLFW_KEY_B }, - // brakeactingspeedsetcargo - { -1 }, - // brakeactingspeedsetpassenger - { -1 }, - // brakeactingspeedsetrapid - { -1 }, - // brakeloadcompensationincrease - { GLFW_KEY_H | keymodifier::shift | keymodifier::control }, - // brakeloadcompensationdecrease - { GLFW_KEY_H | keymodifier::control }, - // mubrakingindicatortoggle - { GLFW_KEY_L | keymodifier::shift }, - // alerteracknowledge - { GLFW_KEY_SPACE }, - // hornlowactivate - { GLFW_KEY_A }, - // hornhighactivate - { GLFW_KEY_S }, - // whistleactivate - { GLFW_KEY_Z }, - // radiotoggle - { GLFW_KEY_R | keymodifier::control }, - // radiochannelincrease - { GLFW_KEY_R | keymodifier::shift }, - // radiochanneldecrease - { GLFW_KEY_R }, - // radiostopsend - { GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control }, - // radiostoptest - { GLFW_KEY_R | keymodifier::shift | keymodifier::control }, - // cabchangeforward - { GLFW_KEY_HOME }, - // cabchangebackward - { GLFW_KEY_END }, - // viewturn - { -1 }, - // movehorizontal - { -1 }, - // movehorizontalfast - { -1 }, - // movevertical - { -1 }, - // moveverticalfast - { -1 }, - // moveleft - { GLFW_KEY_LEFT }, - // moveright - { GLFW_KEY_RIGHT }, - // moveforward - { GLFW_KEY_UP }, - // moveback - { GLFW_KEY_DOWN }, - // moveup - { GLFW_KEY_PAGE_UP }, - // movedown - { GLFW_KEY_PAGE_DOWN }, - // carcouplingincrease - { GLFW_KEY_INSERT }, - // carcouplingdisconnect - { GLFW_KEY_DELETE }, - // doortoggleleft - { GLFW_KEY_COMMA }, - // doortoggleright - { GLFW_KEY_PERIOD }, - // departureannounce - { GLFW_KEY_SLASH }, - // doorlocktoggle - { GLFW_KEY_S | keymodifier::shift }, - // pantographcompressorvalvetoggle - { GLFW_KEY_V | keymodifier::control }, - // pantographcompressoractivate - { GLFW_KEY_V | keymodifier::shift }, - // pantographtogglefront - { GLFW_KEY_P }, - // pantographtogglerear - { GLFW_KEY_O }, - // pantographraisefront - { -1 }, - // pantographraiserear - { -1 }, - // pantographlowerfront - { -1 }, - // pantographlowerrear - { -1 }, - // pantographlowerall - { GLFW_KEY_P | keymodifier::control }, - // heatingtoggle - { GLFW_KEY_H }, - // heatingenable - { -1 }, - // heatingdisable - { -1 }, - // lightspresetactivatenext - { GLFW_KEY_T | keymodifier::shift }, - // lightspresetactivateprevious - { GLFW_KEY_T }, - // headlighttoggleleft - { GLFW_KEY_Y }, - // headlightenableleft - { -1 }, - // headlightdisableleft - { -1 }, - // headlighttoggleright - { GLFW_KEY_I }, - // headlightenableright - { -1 }, - // headlightdisableright - { -1 }, - // headlighttoggleupper - { GLFW_KEY_U }, - // headlightenableupper - { -1 }, - // headlightdisableupper - { -1 }, - // redmarkertoggleleft - { GLFW_KEY_Y | keymodifier::shift }, - // redmarkerenableleft - { -1 }, - // redmarkerdisableleft - { -1 }, - // redmarkertoggleright - { GLFW_KEY_I | keymodifier::shift }, - // redmarkerenableright - { -1 }, - // redmarkerdisableright - { -1 }, - // headlighttogglerearleft - { GLFW_KEY_Y | keymodifier::control }, - // headlighttogglerearright - { GLFW_KEY_I | keymodifier::control }, - // headlighttogglerearupper - { GLFW_KEY_U | keymodifier::control }, - // redmarkertogglerearleft - { GLFW_KEY_Y | keymodifier::control | keymodifier::shift }, - // redmarkertogglerearright - { GLFW_KEY_I | keymodifier::control | keymodifier::shift }, - // redmarkerstoggle - { GLFW_KEY_E | keymodifier::shift }, - // endsignalstoggle - { GLFW_KEY_E }, - // headlightsdimtoggle - { GLFW_KEY_L | keymodifier::control }, - // headlightsdimenable - { -1 }, - // headlightsdimdisable - { -1 }, - // motorconnectorsopen - { GLFW_KEY_L }, - // motorconnectorsclose - { -1 }, - // motordisconnect - { GLFW_KEY_E | keymodifier::control }, - // interiorlighttoggle - { GLFW_KEY_APOSTROPHE }, - // interiorlightenable - { -1 }, - // interiorlightdisable - { -1 }, - // interiorlightdimtoggle - { GLFW_KEY_APOSTROPHE | keymodifier::control }, - // interiorlightdimenable - { -1 }, - // interiorlightdimdisable - { -1 }, - // instrumentlighttoggle - { GLFW_KEY_SEMICOLON }, - // instrumentlightenable - { -1 }, - // instrumentlightdisable, - { -1 }, - // "generictoggle0" - { GLFW_KEY_0 }, - // "generictoggle1" - { GLFW_KEY_1 }, - // "generictoggle2" - { GLFW_KEY_2 }, - // "generictoggle3" - { GLFW_KEY_3 }, - // "generictoggle4" - { GLFW_KEY_4 }, - // "generictoggle5" - { GLFW_KEY_5 }, - // "generictoggle6" - { GLFW_KEY_6 }, - // "generictoggle7" - { GLFW_KEY_7 }, - // "generictoggle8" - { GLFW_KEY_8 }, - // "generictoggle9" - { GLFW_KEY_9 }, - // "batterytoggle" - { GLFW_KEY_J }, - // batteryenable - { -1 }, - // batterydisable - { -1 }, - }; - - bind(); +int +keyboard_input::key( int const Key ) const { + return input::keys[ Key ]; } void @@ -551,23 +189,32 @@ keyboard_input::bind() { m_bindings.clear(); - int commandcode{ 0 }; - for( auto const &command : m_commands ) { + for( auto const &bindingsetup : m_bindingsetups ) { - if( command.binding != -1 ) { + if( bindingsetup.binding != -1 ) { m_bindings.emplace( - command.binding, - static_cast( commandcode ) ); + bindingsetup.binding, + bindingsetup.command ); } - ++commandcode; } // cache movement key bindings - m_bindingscache.forward = m_commands[ static_cast( user_command::moveforward ) ].binding; - m_bindingscache.back = m_commands[ static_cast( user_command::moveback ) ].binding; - m_bindingscache.left = m_commands[ static_cast( user_command::moveleft ) ].binding; - m_bindingscache.right = m_commands[ static_cast( user_command::moveright ) ].binding; - m_bindingscache.up = m_commands[ static_cast( user_command::moveup ) ].binding; - m_bindingscache.down = m_commands[ static_cast( user_command::movedown ) ].binding; + m_bindingscache.forward = binding( user_command::moveforward ); + m_bindingscache.back = binding( user_command::moveback ); + m_bindingscache.left = binding( user_command::moveleft ); + m_bindingscache.right = binding( user_command::moveright ); + m_bindingscache.up = binding( user_command::moveup ); + m_bindingscache.down = binding( user_command::movedown ); +} + +int +keyboard_input::binding( user_command const Command ) const { + + for( auto const &binding : m_bindings ) { + if( binding.second == Command ) { + return binding.first; + } + } + return -1; } bool @@ -590,13 +237,13 @@ keyboard_input::poll() { glm::vec2 const movementhorizontal { // x-axis ( Global.shiftState ? 1.f : 0.5f ) * - ( m_keys[ m_bindingscache.left ] != GLFW_RELEASE ? -1.f : - m_keys[ m_bindingscache.right ] != GLFW_RELEASE ? 1.f : + ( input::keys[ m_bindingscache.left ] != GLFW_RELEASE ? -1.f : + input::keys[ m_bindingscache.right ] != GLFW_RELEASE ? 1.f : 0.f ), // z-axis ( Global.shiftState ? 1.f : 0.5f ) * - ( m_keys[ m_bindingscache.forward ] != GLFW_RELEASE ? 1.f : - m_keys[ m_bindingscache.back ] != GLFW_RELEASE ? -1.f : + ( input::keys[ m_bindingscache.forward ] != GLFW_RELEASE ? 1.f : + input::keys[ m_bindingscache.back ] != GLFW_RELEASE ? -1.f : 0.f ) }; if( ( movementhorizontal.x != 0.f || movementhorizontal.y != 0.f ) @@ -616,8 +263,8 @@ keyboard_input::poll() { float const movementvertical { // y-axis ( Global.shiftState ? 1.f : 0.5f ) * - ( m_keys[ m_bindingscache.up ] != GLFW_RELEASE ? 1.f : - m_keys[ m_bindingscache.down ] != GLFW_RELEASE ? -1.f : + ( input::keys[ m_bindingscache.up ] != GLFW_RELEASE ? 1.f : + input::keys[ m_bindingscache.down ] != GLFW_RELEASE ? -1.f : 0.f ) }; if( ( movementvertical != 0.f ) diff --git a/keyboardinput.h b/keyboardinput.h index 0e59b258..aa64ed9e 100644 --- a/keyboardinput.h +++ b/keyboardinput.h @@ -13,19 +13,32 @@ http://mozilla.org/MPL/2.0/. #include #include "command.h" +namespace input { + +extern std::array keys; +extern bool key_alt; +extern bool key_ctrl; +extern bool key_shift; + +} + class keyboard_input { public: // constructors - keyboard_input() { default_bindings(); } + keyboard_input() = default; + +// destructor + virtual ~keyboard_input() = default; // methods + virtual bool - init() { return recall_bindings(); } - bool - recall_bindings(); + init() { return true; } bool key( int const Key, int const Action ); + int + key( int const Key ) const; void poll(); inline @@ -33,7 +46,7 @@ public: command() const { return m_command; } -private: +protected: // types enum keymodifier : int { @@ -41,43 +54,53 @@ private: control = 0x20000 }; - struct command_setup { + struct binding_setup { + user_command command; int binding; }; - typedef std::vector commandsetup_sequence; - typedef std::unordered_map usercommand_map; + using bindingsetup_sequence = std::vector; + +// methods + virtual + void + default_bindings() = 0; + bool + recall_bindings(); + void + bind(); + +// members + bindingsetup_sequence m_bindingsetups; + +private: +// types + using usercommand_map = std::unordered_map; struct bindings_cache { - int forward{ -1 }; - int back{ -1 }; - int left{ -1 }; - int right{ -1 }; - int up{ -1 }; - int down{ -1 }; + int forward { -1 }; + int back { -1 }; + int left { -1 }; + int right { -1 }; + int up { -1 }; + int down { -1 }; }; // methods - void - default_bindings(); - void - bind(); + int + binding( user_command const Command ) const; bool is_movement_key( int const Key ) const; // members - commandsetup_sequence m_commands; user_command m_command { user_command::none }; // last, if any, issued command usercommand_map m_bindings; command_relay m_relay; - bool m_shift { false }; - bool m_ctrl { false }; bindings_cache m_bindingscache; glm::vec2 m_movementhorizontal { 0.f }; float m_movementvertical { 0.f }; - std::array m_keys; }; //--------------------------------------------------------------------------- diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index f18bbee1..36823659 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -19,11 +19,35 @@ {36684428-8a48-435f-bca4-a24d9bfe2587} - - {cdf75bec-91f7-413c-8b57-9e32cba49148} + + {035056ae-b8d6-4338-9d1f-514a0ce5f1e0} - - {2d73d7b2-5252-499c-963a-88fa3cb1af53} + + {395be8b7-094b-4e4d-918e-02369cd6013b} + + + {1ea7a1f1-5728-478c-8593-b6f4eecefdbf} + + + {89843d2c-930c-4135-8575-714040f117ac} + + + {ab9c6f13-eb8f-497b-b21d-66f8c0220cf8} + + + {492181c1-fa1a-4087-be99-5d88edf66c69} + + + {e8c50720-4d83-4722-8ac9-16ea6af834b5} + + + {ecadb5ae-48aa-4811-909d-56e8a01badb2} + + + {ba04e497-9bfa-4cfd-883f-7f018ac76301} + + + {0630616b-1afe-4fb6-96d1-3755834ba8aa} @@ -120,27 +144,18 @@ Source Files - - Source Files - Source Files\mczapkie Source Files\mczapkie - - Source Files\input - Source Files\mczapkie Source Files\mczapkie - - Source Files\input - Source Files @@ -180,21 +195,12 @@ Source Files - - Source Files\input - - - Source Files\input - Source Files Source Files - - Source Files\input - Source Files @@ -231,27 +237,72 @@ Source Files - - Source Files\input - Source Files Source Files - - Source Files - Source Files - - Source Files - Source Files + + Source Files\application + + + Source Files + + + Source Files + + + Source Files + + + Source Files\application\mode_driver + + + Source Files\application\mode_driver + + + Source Files\application\mode_editor + + + Source Files\application\mode_editor + + + Source Files\application\mode_scenarioloader + + + Source Files\application\mode_driver + + + Source Files\application\mode_driver + + + Source Files\application\mode_editor + + + Source Files\application\mode_driver + + + Source Files\application\mode_driver + + + Source Files\application\mode_driver + + + Source Files\application\mode_driver + + + Source Files\application\mode_editor + + + Source Files\application\input + @@ -266,9 +317,6 @@ Header Files\mczapkie - - Header Files - Header Files @@ -317,12 +365,6 @@ Header Files\mczapkie - - Header Files\input - - - Header Files\input - Header Files @@ -419,21 +461,12 @@ Header Files - - Header Files\input - - - Header Files\input - Header Files Header Files - - Header Files\input - Header Files @@ -473,21 +506,72 @@ Header Files - - Header Files\input - Header Files - - Header Files - Header Files + + Header Files\application + + Header Files\application + + Header Files + + Header Files + + + Header Files + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_driver + + + Header Files\application\mode_editor + + + Header Files\application\mode_editor + + + Header Files\application\mode_editor + + + Header Files\application\mode_scenarioloader + + + Header Files\application\mode_scenarioloader + + + Header Files\application\mode_editor + + + Header Files\application\input + diff --git a/messaging.cpp b/messaging.cpp index 8d081b57..57ef9977 100644 --- a/messaging.cpp +++ b/messaging.cpp @@ -13,6 +13,10 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "application.h" #include "simulation.h" +#include "simulationtime.h" +#include "event.h" +#include "dynobj.h" +#include "driver.h" #include "mtable.h" #include "logs.h" @@ -36,6 +40,165 @@ Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) { #endif } +void +OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) +{ // odebranie komunikatu z serwera + if (pRozkaz->iSygn == MAKE_ID4('E','U','0','7') ) + switch (pRozkaz->iComm) + { + case 0: // odesłanie identyfikatora wersji + CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " version" + " rcvd"); + WyslijString(Global.asVersion, 0); // przedsatwienie się + break; + case 1: // odesłanie identyfikatora wersji + CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " scenery" + " rcvd"); + WyslijString(Global.SceneryFile, 1); // nazwa scenerii + break; + case 2: { + // event + CommLog( Now() + " " + std::to_string( pRozkaz->iComm ) + " " + + std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" ); + + if( Global.iMultiplayer ) { + auto *event = simulation::Events.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); + if( event != nullptr ) { + if( ( event->Type == tp_Multiple ) + || ( event->Type == tp_Lights ) + || ( event->evJoined != 0 ) ) { + // tylko jawne albo niejawne Multiple + simulation::Events.AddToQuery( event, nullptr ); // drugi parametr to dynamic wywołujący - tu brak + } + } + } + break; + } + case 3: // rozkaz dla AI + if (Global.iMultiplayer) + { + int i = int(pRozkaz->cString[8]); // długość pierwszego łańcucha (z przodu dwa floaty) + CommLog( + Now() + " " + to_string(pRozkaz->iComm) + " " + + std::string(pRozkaz->cString + 11 + i, (unsigned)(pRozkaz->cString[10 + i])) + + " rcvd"); + // nazwa pojazdu jest druga + auto *vehicle = simulation::Vehicles.find( { pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[ 10 + i ] } ); + if( ( vehicle != nullptr ) + && ( vehicle->Mechanik != nullptr ) ) { + vehicle->Mechanik->PutCommand( + { pRozkaz->cString + 9, static_cast(i) }, + pRozkaz->fPar[0], pRozkaz->fPar[1], + nullptr, + stopExt ); // floaty są z przodu + WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); + } + } + break; + case 4: // badanie zajętości toru + { + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); + + auto *track = simulation::Paths.find( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); + if( ( track != nullptr ) + && ( track->IsEmpty() ) ) { + WyslijWolny( track->name() ); + } + } + break; + case 5: // ustawienie parametrów + { + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " params " + to_string(*pRozkaz->iPar) + " rcvd"); + if (*pRozkaz->iPar == 0) // sprawdzenie czasu + if (*pRozkaz->iPar & 1) // ustawienie czasu + { + double t = pRozkaz->fPar[1]; + simulation::Time.data().wDay = std::floor(t); // niby nie powinno być dnia, ale... + if (Global.fMoveLight >= 0) + Global.fMoveLight = t; // trzeba by deklinację Słońca przeliczyć + simulation::Time.data().wHour = std::floor(24 * t) - 24.0 * simulation::Time.data().wDay; + simulation::Time.data().wMinute = std::floor(60 * 24 * t) - 60.0 * (24.0 * simulation::Time.data().wDay + simulation::Time.data().wHour); + simulation::Time.data().wSecond = std::floor( 60 * 60 * 24 * t ) - 60.0 * ( 60.0 * ( 24.0 * simulation::Time.data().wDay + simulation::Time.data().wHour ) + simulation::Time.data().wMinute ); + } + if (*pRozkaz->iPar & 2) + { // ustawienie flag zapauzowania + Global.iPause = pRozkaz->fPar[2]; // zakładamy, że wysyłający wie, co robi + } + } + break; + case 6: // pobranie parametrów ruchu pojazdu + if (Global.iMultiplayer) { + // Ra 2014-12: to ma działać również dla pojazdów bez obsady + CommLog( + Now() + " " + + to_string( pRozkaz->iComm ) + " " + + std::string{ pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) } + + " rcvd" ); + if (pRozkaz->cString[0]) { + // jeśli długość nazwy jest niezerowa szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7 + auto *vehicle = ( + pRozkaz->cString[ 1 ] == '*' ? + simulation::Vehicles.find( Global.asHumanCtrlVehicle ) : + simulation::Vehicles.find( std::string{ pRozkaz->cString + 1, (unsigned)pRozkaz->cString[ 0 ] } ) ); + if( vehicle != nullptr ) { + WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe + } + } + else { + // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, to rozpoznać np. "*") + simulation::Vehicles.DynamicList(); + } + } + break; + case 8: // ponowne wysłanie informacji o zajętych odcinkach toru + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy track" + " rcvd"); + simulation::Paths.TrackBusyList(); + break; + case 9: // ponowne wysłanie informacji o zajętych odcinkach izolowanych + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy isolated" + " rcvd"); + simulation::Paths.IsolatedBusyList(); + break; + case 10: // badanie zajętości jednego odcinka izolowanego + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); + simulation::Paths.IsolatedBusy( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); + break; + case 11: // ustawienie parametrów ruchu pojazdu + // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); + break; + case 12: // skrocona ramka parametrow pojazdow AI (wszystkich!!) + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " obsadzone" + " rcvd"); + WyslijObsadzone(); + // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); + break; + case 13: // ramka uszkodzenia i innych stanow pojazdu, np. wylaczenie CA, wlaczenie recznego itd. + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + + " rcvd"); + if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa + { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 + auto *lookup = ( + pRozkaz->cString[ 2 ] == '*' ? + simulation::Vehicles.find( Global.asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika + simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu + if( lookup == nullptr ) { break; } // nothing found, nothing to do + auto *d { lookup }; + while( d != nullptr ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Next(); // pozostałe też + } + d = lookup->Prev(); + while( d != nullptr ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Prev(); // w drugą stronę też + } + WyslijUszkodzenia( lookup->asName, lookup->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe + } + break; + default: + break; + } +} + void WyslijEvent(const std::string &e, const std::string &d) { // Ra: jeszcze do wyczyszczenia diff --git a/messaging.h b/messaging.h index 6bd7a0e3..46624bfe 100644 --- a/messaging.h +++ b/messaging.h @@ -37,6 +37,8 @@ struct DaneRozkaz2 { // struktura komunikacji z EU07.EXE void Navigate( std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam ); +void OnCommandGet( multiplayer::DaneRozkaz *pRozkaz ); + void WyslijEvent( const std::string &e, const std::string &d ); void WyslijString( const std::string &t, int n ); void WyslijWolny( const std::string &t ); diff --git a/World.cpp b/old/World.cpp similarity index 100% rename from World.cpp rename to old/World.cpp diff --git a/World.h b/old/World.h similarity index 100% rename from World.h rename to old/World.h diff --git a/renderer.cpp b/renderer.cpp index 808a5e75..e39db423 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -12,15 +12,15 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "color.h" #include "globals.h" +#include "camera.h" #include "timer.h" #include "simulation.h" #include "simulationtime.h" -#include "world.h" #include "train.h" #include "dynobj.h" #include "animmodel.h" #include "traction.h" -#include "uilayer.h" +#include "application.h" #include "logs.h" #include "utilities.h" @@ -128,7 +128,7 @@ opengl_renderer::Init( GLFWwindow *Window ) { std::vector{ m_diffusetextureunit } : std::vector{ m_normaltextureunit, m_diffusetextureunit } ); m_textures.assign_units( m_helpertextureunit, m_shadowtextureunit, m_normaltextureunit, m_diffusetextureunit ); // TODO: add reflections unit - UILayer.set_unit( m_diffusetextureunit ); + ui_layer::set_unit( m_diffusetextureunit ); select_unit( m_diffusetextureunit ); ::glDepthFunc( GL_LEQUAL ); @@ -397,7 +397,7 @@ opengl_renderer::Render() { Timer::subsystem.gfx_total.start(); // note: gfx_total is actually frame total, clean this up Timer::subsystem.gfx_color.start(); // fetch simulation data - if( World.InitPerformed() ) { + if( simulation::is_ready ) { m_sunlight = Global.DayLight; // quantize sun angle to reduce shadow crawl auto const quantizationstep { 0.004f }; @@ -453,7 +453,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( ( true == Global.RenderShadows ) && ( false == Global.bWireFrame ) - && ( true == World.InitPerformed() ) + && ( true == simulation::is_ready ) && ( m_shadowcolor != colors::white ) ) { // run shadowmaps pass before color @@ -492,7 +492,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } if( ( true == m_environmentcubetexturesupport ) - && ( true == World.InitPerformed() ) ) { + && ( true == simulation::is_ready ) ) { // potentially update environmental cube map if( true == Render_reflections() ) { setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack @@ -501,8 +501,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glViewport( 0, 0, Global.iWindowWidth, Global.iWindowHeight ); - if( World.InitPerformed() ) { - auto const skydomecolour = World.Environment.m_skydome.GetAverageColor(); + if( simulation::is_ready ) { + auto const skydomecolour = simulation::Environment.m_skydome.GetAverageColor(); ::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.f ); // kolor nieba } else { @@ -510,13 +510,13 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { + if( simulation::is_ready ) { // setup setup_matrices(); // render setup_drawing( true ); setup_units( true, false, false ); - Render( &World.Environment ); + Render( &simulation::Environment ); // opaque parts... setup_drawing( false ); setup_units( true, true, true ); @@ -544,7 +544,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); // cache shadow colour in case we need to account for cab light auto const shadowcolor { m_shadowcolor }; - auto const *vehicle{ World.Train->Dynamic() }; + auto const *vehicle{ simulation::Train->Dynamic() }; if( vehicle->InteriorLightLevel > 0.f ) { setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); } @@ -565,7 +565,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); // cache shadow colour in case we need to account for cab light auto const shadowcolor{ m_shadowcolor }; - auto const *vehicle{ World.Train->Dynamic() }; + auto const *vehicle{ simulation::Train->Dynamic() }; if( vehicle->InteriorLightLevel > 0.f ) { setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); } @@ -584,13 +584,13 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glMatrixMode( GL_MODELVIEW ); } } - UILayer.render(); + Application.render_ui(); break; } case rendermode::shadows: { - if( World.InitPerformed() ) { + if( simulation::is_ready ) { // setup ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting ::glPolygonOffset( 1.f, 1.f ); @@ -631,7 +631,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::cabshadows: { - if( ( World.InitPerformed() ) + if( ( simulation::is_ready ) && ( false == FreeFlyModeFlag ) ) { // setup ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting @@ -659,8 +659,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #else setup_units( false, false, false ); #endif - Render_cab( World.Train->Dynamic(), false ); - Render_cab( World.Train->Dynamic(), true ); + Render_cab( simulation::Train->Dynamic(), false ); + Render_cab( simulation::Train->Dynamic(), true ); m_cabshadowpass = m_renderpass; // post-render restore @@ -678,7 +678,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glClearColor( 0.f, 0.f, 0.f, 1.f ); ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { + if( simulation::is_ready ) { ::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE ); // setup @@ -686,7 +686,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // render setup_drawing( true ); setup_units( true, false, false ); - Render( &World.Environment ); + Render( &simulation::Environment ); // opaque parts... setup_drawing( false ); setup_units( true, true, true ); @@ -708,7 +708,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } case rendermode::pickcontrols: { - if( World.InitPerformed() ) { + if( simulation::is_ready ) { // setup #ifdef EU07_USE_PICKING_FRAMEBUFFER ::glViewport( 0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); @@ -723,14 +723,14 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_drawing( false ); setup_units( false, false, false ); // cab render skips translucent parts, so we can do it here - if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } + if( simulation::Train != nullptr ) { Render_cab( simulation::Train->Dynamic() ); } // post-render cleanup } break; } case rendermode::pickscenery: { - if( World.InitPerformed() ) { + if( simulation::is_ready ) { // setup m_picksceneryitems.clear(); #ifdef EU07_USE_PICKING_FRAMEBUFFER @@ -789,12 +789,12 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f Config.draw_mode = Mode; - if( false == World.InitPerformed() ) { return; } + if( false == simulation::is_ready ) { return; } // setup draw range switch( Mode ) { case rendermode::color: { Config.draw_range = Global.BaseDrawRange; break; } case rendermode::shadows: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; } - case rendermode::cabshadows: { Config.draw_range = ( Global.pWorld->train()->Dynamic()->MoverParameters->ActiveCab != 0 ? 10.f : 20.f ); break; } + case rendermode::cabshadows: { Config.draw_range = ( simulation::Train->Occupied()->ActiveCab != 0 ? 10.f : 20.f ); break; } case rendermode::reflections: { Config.draw_range = Global.BaseDrawRange; break; } case rendermode::pickcontrols: { Config.draw_range = 50.f; break; } case rendermode::pickscenery: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; } @@ -811,11 +811,11 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f // modelview if( ( false == DebugCameraFlag ) || ( true == Ignoredebug ) ) { camera.position() = Global.pCameraPosition; - World.Camera.SetMatrix( viewmatrix ); + Global.pCamera->SetMatrix( viewmatrix ); } else { camera.position() = Global.DebugCameraPosition; - World.DebugCamera.SetMatrix( viewmatrix ); + Global.pDebugCamera->SetMatrix( viewmatrix ); } // projection auto const zfar = Config.draw_range * Global.fDistanceFactor * Zfar; @@ -972,7 +972,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f // TODO: scissor test for pick modes // modelview camera.position() = Global.pCameraPosition; - World.Camera.SetMatrix( viewmatrix ); + Global.pCamera->SetMatrix( viewmatrix ); // projection camera.projection() *= glm::perspective( @@ -1670,7 +1670,7 @@ opengl_renderer::Render( scene::basic_region *Region ) { Update_Lights( simulation::Lights ); Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); - if( EditorModeFlag && FreeFlyModeFlag ) { + if( EditorModeFlag ) { // when editor mode is active calculate world position of the cursor // at this stage the z-buffer is filled with only ground geometry Update_Mouse_Position(); @@ -2099,7 +2099,7 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { ++m_debugstats.dynamics; // setup - TSubModel::iInstance = ( size_t )Dynamic; //żeby nie robić cudzych animacji + TSubModel::iInstance = reinterpret_cast( Dynamic ); //żeby nie robić cudzych animacji glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); // lod visibility ranges are defined for base (x 1.0) viewing distance. for render we adjust them for actual range multiplier and zoom float squaredistance; @@ -2201,7 +2201,7 @@ opengl_renderer::Render_cab( TDynamicObject const *Dynamic, bool const Alpha ) { return false; } - TSubModel::iInstance = reinterpret_cast( Dynamic ); + TSubModel::iInstance = reinterpret_cast( Dynamic ); if( ( true == FreeFlyModeFlag ) || ( false == Dynamic->bDisplayCab ) @@ -3487,7 +3487,8 @@ glm::dvec3 opengl_renderer::Update_Mouse_Position() { glm::dvec2 mousepos; - glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); + Application.get_cursor_pos( mousepos.x, mousepos.y ); +// glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); mousepos.x = clamp( mousepos.x, 0, Global.iWindowWidth - 1 ); mousepos.y = clamp( Global.iWindowHeight - clamp( mousepos.y, 0, Global.iWindowHeight ), 0, Global.iWindowHeight - 1 ) ; GLfloat pointdepth; @@ -3550,7 +3551,7 @@ opengl_renderer::Update( double const Deltatime ) { } if( ( true == Global.ResourceSweep ) - && ( true == World.InitPerformed() ) ) { + && ( true == simulation::is_ready ) ) { // garbage collection m_geometry.update(); m_textures.update(); diff --git a/renderer.h b/renderer.h index 3db03799..6c47ad05 100644 --- a/renderer.h +++ b/renderer.h @@ -17,7 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "dumb3d.h" #include "frustum.h" #include "scene.h" -#include "world.h" +#include "simulationenvironment.h" #include "memcell.h" #define EU07_USE_PICKING_FRAMEBUFFER diff --git a/scenarioloadermode.cpp b/scenarioloadermode.cpp new file mode 100644 index 00000000..128ce548 --- /dev/null +++ b/scenarioloadermode.cpp @@ -0,0 +1,79 @@ +/* +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 "scenarioloadermode.h" + +#include "globals.h" +#include "simulation.h" +#include "simulationtime.h" +#include "timer.h" +#include "application.h" +#include "scenarioloaderuilayer.h" +#include "renderer.h" +#include "logs.h" + + +scenarioloader_mode::scenarioloader_mode() { + + m_userinterface = std::make_shared(); +} + +// initializes internal data structures of the mode. returns: true on success, false otherwise +bool +scenarioloader_mode::init() { + // nothing to do here + return true; +} + +// mode-specific update of simulation data. returns: false on error, true otherwise +bool +scenarioloader_mode::update() { + + WriteLog( "\nLoading scenario..." ); + + auto timestart = std::chrono::system_clock::now(); + + if( true == simulation::State.deserialize( Global.SceneryFile ) ) { + WriteLog( "Scenario loading time: " + std::to_string( std::chrono::duration_cast( ( std::chrono::system_clock::now() - timestart ) ).count() ) + " seconds" ); + // TODO: implement and use next mode cue + Application.pop_mode(); + Application.push_mode( eu07_application::mode::driver ); + } + else { + ErrorLog( "Bad init: scenario loading failed" ); + Application.pop_mode(); + } + + return true; +} + +// maintenance method, called when the mode is activated +void +scenarioloader_mode::enter() { + + // TBD: hide cursor in fullscreen mode? + Application.set_cursor( GLFW_CURSOR_NORMAL ); + + simulation::is_ready = false; + + m_userinterface->set_background( "logo" ); + Application.set_title( Global.AppName + " (" + Global.SceneryFile + ")" ); + m_userinterface->set_progress(); + m_userinterface->set_progress( "Loading scenery / Wczytywanie scenerii" ); + GfxRenderer.Render(); +} + +// maintenance method, called when the mode is deactivated +void +scenarioloader_mode::exit() { + + simulation::Time.init(); + simulation::Environment.init(); +} diff --git a/scenarioloadermode.h b/scenarioloadermode.h new file mode 100644 index 00000000..18c603d8 --- /dev/null +++ b/scenarioloadermode.h @@ -0,0 +1,41 @@ +/* +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 + +#include "applicationmode.h" + +class scenarioloader_mode : public application_mode { + +public: +// constructors + scenarioloader_mode(); +// methods + // initializes internal data structures of the mode. returns: true on success, false otherwise + bool + init() override; + // mode-specific update of simulation data. returns: false on error, true otherwise + bool + update() override; + // maintenance method, called when the mode is activated + void + enter() override; + // maintenance method, called when the mode is deactivated + void + exit() override; + // input handlers + void + on_key( int const Key, int const Scancode, int const Action, int const Mods ) override { ; } + void + on_cursor_pos( double const Horizontal, double const Vertical ) override { ; } + void + on_mouse_button( int const Button, int const Action, int const Mods ) override { ; } + void + on_scroll( double const Xoffset, double const Yoffset ) override { ; } +}; diff --git a/scenarioloaderuilayer.h b/scenarioloaderuilayer.h new file mode 100644 index 00000000..c167a444 --- /dev/null +++ b/scenarioloaderuilayer.h @@ -0,0 +1,17 @@ +/* +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 + +#include "uilayer.h" + +class scenarioloader_ui : public ui_layer { + + // TODO: implement mode-specific elements +}; diff --git a/scene.cpp b/scene.cpp index 003e2622..f12054b8 100644 --- a/scene.cpp +++ b/scene.cpp @@ -12,6 +12,9 @@ http://mozilla.org/MPL/2.0/. #include "simulation.h" #include "globals.h" +#include "animmodel.h" +#include "event.h" +#include "evlaunch.h" #include "timer.h" #include "logs.h" #include "sn_utils.h" diff --git a/sceneeditor.cpp b/sceneeditor.cpp index 3042196c..c79d4bb0 100644 --- a/sceneeditor.cpp +++ b/sceneeditor.cpp @@ -13,97 +13,19 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "application.h" #include "simulation.h" +#include "camera.h" +#include "animmodel.h" #include "renderer.h" namespace scene { -basic_editor Editor; - -bool -basic_editor::on_key( int const Key, int const Action ) { - - if( false == EditorModeFlag ) { return false; } - - if( ( Key == GLFW_KEY_LEFT_ALT ) - || ( Key == GLFW_KEY_RIGHT_ALT ) ) { - // intercept these while in editor mode - return true; - } - - return false; -} - -bool -basic_editor::on_mouse_button( int const Button, int const Action ) { - - if( false == EditorModeFlag ) { return false; } - // TBD: automatically activate and enforce freefly mode when editor is active? - if( false == FreeFlyModeFlag ) { return false; } - - if( Button == GLFW_MOUSE_BUTTON_LEFT ) { - - if( Action == GLFW_PRESS ) { - - m_node = GfxRenderer.Update_Pick_Node(); - m_nodesnapshot = { m_node }; - if( m_node ) { - Application.set_cursor( GLFW_CURSOR_DISABLED ); - } - } - else { - // left button release - // TODO: record the current undo step on the undo stack - m_nodesnapshot = { m_node }; - if( m_node ) { - Application.set_cursor( GLFW_CURSOR_NORMAL ); - } - } - - m_mouseleftbuttondown = ( Action == GLFW_PRESS ); - - return ( m_node != nullptr ); - } - - return false; -} - -bool -basic_editor::on_mouse_move( double const Mousex, double const Mousey ) { - - auto const mousemove { glm::dvec2{ Mousex, Mousey } - m_mouseposition }; - m_mouseposition = { Mousex, Mousey }; - - if( false == EditorModeFlag ) { return false; } - if( false == m_mouseleftbuttondown ) { return false; } - if( m_node == nullptr ) { return false; } - - if( mode_translation() ) { - // move selected node - if( mode_translation_vertical() ) { - auto const translation { mousemove.y * -0.01f }; - translate( translation ); - } - else { - auto const mouseworldposition{ Global.pCamera->Pos + GfxRenderer.Mouse_Position() }; - translate( mouseworldposition ); - } - } - else { - // rotate selected node - auto const rotation { glm::vec3 { mousemove.y, mousemove.x, 0 } * 0.25f }; - rotate( rotation ); - } - - return true; -} - void -basic_editor::translate( glm::dvec3 const &Location ) { +basic_editor::translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground ) { - auto *node { m_node }; // placeholder for operations on multiple nodes + auto *node { Node }; // placeholder for operations on multiple nodes auto location { Location }; - if( false == mode_snap() ) { + if( false == Snaptoground ) { location.y = node->location().y; } @@ -117,14 +39,14 @@ basic_editor::translate( glm::dvec3 const &Location ) { } void -basic_editor::translate( float const Offset ) { +basic_editor::translate( scene::basic_node *Node, float const Offset ) { // NOTE: offset scaling is calculated early so the same multiplier can be applied to potential whole group - auto location { m_node->location() }; + auto location { Node->location() }; auto const distance { glm::length( location - glm::dvec3{ Global.pCamera->Pos } ) }; auto const offset { Offset * std::max( 1.0, distance * 0.01 ) }; - auto *node { m_node }; // placeholder for operations on multiple nodes + auto *node { Node }; // placeholder for operations on multiple nodes if( typeid( *node ) == typeid( TAnimModel ) ) { translate_instance( static_cast( node ), offset ); @@ -167,47 +89,29 @@ basic_editor::translate_memorycell( TMemCell *Memorycell, float const Offset ) { } void -basic_editor::rotate( glm::vec3 const &Angle ) { +basic_editor::rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization ) { - auto *node { m_node }; // placeholder for operations on multiple nodes + auto *node { Node }; // placeholder for operations on multiple nodes if( typeid( *node ) == typeid( TAnimModel ) ) { - rotate_instance( static_cast( node ), Angle ); + rotate_instance( static_cast( node ), Angle, Quantization ); } } void -basic_editor::rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle ) { +basic_editor::rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle, float const Quantization ) { // adjust node data glm::vec3 angle = glm::dvec3 { Instance->Angles() }; angle.y = clamp_circular( angle.y + Angle.y, 360.f ); - if( mode_snap() ) { + if( Quantization > 0.f ) { // TBD, TODO: adjustable quantization step - angle.y = quantize( angle.y, 15.f ); + angle.y = quantize( angle.y, Quantization ); } Instance->Angles( angle ); // update scene } -bool -basic_editor::mode_translation() const { - - return ( false == Global.altState ); -} - -bool -basic_editor::mode_translation_vertical() const { - - return ( true == Global.shiftState ); -} - -bool -basic_editor::mode_snap() const { - - return ( true == Global.ctrlState ); -} - } // scene //--------------------------------------------------------------------------- diff --git a/sceneeditor.h b/sceneeditor.h index b69874b8..583bce27 100644 --- a/sceneeditor.h +++ b/sceneeditor.h @@ -13,44 +13,29 @@ http://mozilla.org/MPL/2.0/. namespace scene { +// TODO: move the snapshot to history stack +struct node_snapshot { + + scene::basic_node *node; + std::string data; + + node_snapshot( scene::basic_node *Node ) : + node( Node ) { + if( Node != nullptr ) { + Node->export_as_text( data ); } }; +}; + +inline bool operator==( node_snapshot const &Left, node_snapshot const &Right ) { return ( ( Left.node == Right.node ) && ( Left.data == Right.data ) ); } +inline bool operator!=( node_snapshot const &Left, node_snapshot const &Right ) { return ( !( Left == Right ) ); } + class basic_editor { public: // methods - bool - on_key( int const Key, int const Action ); - bool - on_mouse_button( int const Button, int const Action ); - bool - on_mouse_move( double const Mousex, double const Mousey ); - scene::basic_node const * - node() const { - return m_node; } -private: -// types - struct node_snapshot { - - scene::basic_node *node; - std::string data; - - node_snapshot( scene::basic_node *Node ) : - node( Node ) { - if( Node != nullptr ) { - Node->export_as_text( data ); } }; - }; - friend bool operator==( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right ); - friend bool operator!=( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right ); -// methods - bool - mode_translation() const; - bool - mode_translation_vertical() const; - bool - mode_snap() const; void - translate( glm::dvec3 const &Location ); + translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground ); void - translate( float const Offset ); + translate( scene::basic_node *Node, float const Offset ); void translate_instance( TAnimModel *Instance, glm::dvec3 const &Location ); void @@ -60,22 +45,11 @@ private: void translate_memorycell( TMemCell *Memorycell, float const Offset ); void - rotate( glm::vec3 const &Angle ); + rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization ); void - rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle ); -// members - scene::basic_node *m_node; // temporary helper, currently selected scene node - node_snapshot m_nodesnapshot { nullptr }; // currently selected scene node in its pre-modified state - glm::dvec2 m_mouseposition { 0.0 }; - bool m_mouseleftbuttondown { false }; - + rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle, float const Quantization ); }; -inline bool operator==( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right ) { return ( ( Left.node == Right.node ) && ( Left.data == Right.data ) ); } -inline bool operator!=( basic_editor::node_snapshot const &Left, basic_editor::node_snapshot const &Right ) { return ( !( Left == Right ) ); } - -extern basic_editor Editor; - } // scene //--------------------------------------------------------------------------- diff --git a/simulation.cpp b/simulation.cpp index a3a076f4..779ce554 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -9,13 +9,20 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "simulation.h" +#include "simulationtime.h" #include "globals.h" -#include "simulationtime.h" -#include "uilayer.h" -#include "renderer.h" -#include "logs.h" - +#include "event.h" +#include "memcell.h" +#include "track.h" +#include "traction.h" +#include "tractionpower.h" +#include "sound.h" +#include "animmodel.h" +#include "dynobj.h" +#include "lightarray.h" +#include "scene.h" +#include "train.h" namespace simulation { @@ -31,8 +38,9 @@ vehicle_table Vehicles; light_array Lights; scene::basic_region *Region { nullptr }; +TTrain *Train { nullptr }; - +bool is_ready { false }; bool state_manager::deserialize( std::string const &Scenariofile ) { @@ -63,909 +71,27 @@ state_manager::update( double const Deltatime, int Iterationcount ) { 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(); - - // TODO: check first for presence of serialized binary files - // if this fails, fall back on the legacy text format - scene::scratch_data importscratchpad; - if( Scenariofile != "$.scn" ) { - // compilation to binary file isn't supported for rainsted-created overrides - importscratchpad.binary.terrain = Region->deserialize( Scenariofile ); - } - // NOTE: for the time being import from text format is a given, since we don't have full binary serialization - cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction ); - - if( false == scenarioparser.ok() ) { return false; } - - deserialize( scenarioparser, importscratchpad ); - if( ( false == importscratchpad.binary.terrain ) - && ( Scenariofile != "$.scn" ) ) { - // if we didn't find usable binary version of the scenario files, create them now for future use - // as long as the scenario file wasn't rainsted-created base file override - Region->serialize( Scenariofile ); - } - return true; -} - -// restores class data from provided stream void -state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { +state_manager::update_clocks() { - // 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_serializer::*)(cParser &, scene::scratch_data &); - std::vector< - std::pair< - std::string, - deserializefunction> > functionlist = { - { "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, - deserializefunctionbind> functionmap; - for( auto &function : functionlist ) { - functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) ); - } - - // deserialize content from the provided input - auto - timelast { std::chrono::steady_clock::now() }, - timenow { timelast }; - std::string token { Input.getToken() }; - while( false == token.empty() ) { - - auto lookup = functionmap.find( token ); - if( lookup != functionmap.end() ) { - lookup->second(); - } - else { - ErrorLog( "Bad scenario: unexpected token \"" + token + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); - } - - timenow = std::chrono::steady_clock::now(); - if( std::chrono::duration_cast( timenow - timelast ).count() >= 200 ) { - timelast = timenow; - glfwPollEvents(); - UILayer.set_progress( Input.getProgress(), Input.getFullProgress() ); - GfxRenderer.Render(); - } - - token = Input.getToken(); - } - - if( false == Scratchpad.initialized ) { - // manually perform scenario initialization - deserialize_firstinit( Input, Scratchpad ); - } + // Ra 2014-07: przeliczenie kąta czasu (do animacji zależnych od czasu) + auto const &time = simulation::Time.data(); + Global.fTimeAngleDeg = time.wHour * 15.0 + time.wMinute * 0.25 + ( ( time.wSecond + 0.001 * time.wMilliseconds ) / 240.0 ); + Global.fClockAngleDeg[ 0 ] = 36.0 * ( time.wSecond % 10 ); // jednostki sekund + Global.fClockAngleDeg[ 1 ] = 36.0 * ( time.wSecond / 10 ); // dziesiątki sekund + Global.fClockAngleDeg[ 2 ] = 36.0 * ( time.wMinute % 10 ); // jednostki minut + Global.fClockAngleDeg[ 3 ] = 36.0 * ( time.wMinute / 10 ); // dziesiątki minut + Global.fClockAngleDeg[ 4 ] = 36.0 * ( time.wHour % 10 ); // jednostki godzin + Global.fClockAngleDeg[ 5 ] = 36.0 * ( time.wHour / 10 ); // dziesiątki godzin } +// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel void -state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) { +radio_message( sound_source *Message, int const Channel ) { - // 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 - Input.getTokens( 3 ); - // fog range - Input.getTokens( 2 ); - Input - >> Global.fFogStart - >> Global.fFogEnd; - - if( Global.fFogEnd > 0.0 ) { - // fog colour; optional legacy parameter, no longer used - Input.getTokens( 3 ); + if( Train != nullptr ) { + Train->radio_message( Message, Channel ); } - - std::string token { Input.getToken() }; - if( token != "endatmo" ) { - // optional overcast parameter - Global.Overcast = clamp( std::stof( token ), 0.f, 2.f ); - // overcast drives weather so do a calculation here - // NOTE: ugly, clean it up when we're done with world refactoring - Global.pWorld->compute_weather(); - } - while( ( false == token.empty() ) - && ( token != "endatmo" ) ) { - // anything else left in the section has no defined meaning - token = Input.getToken(); - } -} - -void -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ć - std::string token; - do { // opcjonalna siódma liczba określa numer kamery, a kiedyś były tylko 3 - Input.getTokens(); - Input >> token; - switch( ++i ) { // kiedyś camera miało tylko 3 współrzędne - case 0: { xyz.x = atof( token.c_str() ); break; } - case 1: { xyz.y = atof( token.c_str() ); break; } - case 2: { xyz.z = atof( token.c_str() ); break; } - case 3: { abc.x = atof( token.c_str() ); break; } - case 4: { abc.y = atof( token.c_str() ); break; } - case 5: { abc.z = atof( token.c_str() ); break; } - case 6: { into = atoi( token.c_str() ); break; } // takie sobie, bo można wpisać -1 - default: { break; } - } - } while( token.compare( "endcamera" ) != 0 ); - if( into < 0 ) - into = ++Global.iCameraLast; - if( into < 10 ) { // przepisanie do odpowiedniego miejsca w tabelce - Global.FreeCameraInit[ into ] = xyz; - Global.FreeCameraInitAngle[ into ] = - Math3D::vector3( - glm::radians( abc.x ), - glm::radians( abc.y ), - glm::radians( abc.z ) ); - Global.iCameraLast = into; // numer ostatniej - } -/* - // cleaned up version of the above. - // NOTE: no longer supports legacy mode where some parameters were optional - Input.getTokens( 7 ); - glm::vec3 - position, - rotation; - int index; - Input - >> position.x - >> position.y - >> position.z - >> rotation.x - >> rotation.y - >> rotation.z - >> index; - - skip_until( Input, "endcamera" ); - - // TODO: finish this -*/ -} - -void -state_serializer::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) { - - // config parameters (re)definition - Global.ConfigParse( Input ); -} - -void -state_serializer::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) { - - // legacy section, never really used; - skip_until( Input, "enddescription" ); -} - -void -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(); - Math3D::vector3 offset = ( - Scratchpad.location.offset.empty() ? - Math3D::vector3() : - Math3D::vector3( - Scratchpad.location.offset.top().x, - Scratchpad.location.offset.top().y, - Scratchpad.location.offset.top().z ) ); - event->Load( &Input, offset ); - - if( false == simulation::Events.insert( event ) ) { - delete event; - } -} - -void -state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { - - if( true == Scratchpad.initialized ) { return; } - - simulation::Paths.InitTracks(); - simulation::Traction.InitTraction(); - simulation::Events.InitEvents(); - simulation::Events.InitLaunchers(); - simulation::Memory.InitCells(); - - Scratchpad.initialized = true; -} - -void -state_serializer::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) { - - // legacy section, no longer used nor supported; - skip_until( Input, "endlight" ); -} - -void -state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) { - - auto const inputline = Input.Line(); // cache in case we need to report error - - scene::node_data nodedata; - // common data and node type indicator - Input.getTokens( 4 ); - Input - >> nodedata.range_max - >> nodedata.range_min - >> nodedata.name - >> nodedata.type; - // type-based deserialization. not elegant but it'll do - if( nodedata.type == "dynamic" ) { - - auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) }; - // vehicle import can potentially fail - if( vehicle == nullptr ) { return; } - - if( false == simulation::Vehicles.insert( vehicle ) ) { - - ErrorLog( "Bad scenario: vehicle with duplicate name \"" + vehicle->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } - - if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only - && ( ( ( vehicle->LightList( side::front ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) - || ( ( vehicle->LightList( side::rear ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) ) ) { - simulation::Lights.insert( vehicle ); - } - } - else if( nodedata.type == "track" ) { - - auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; - // duplicates of named tracks are currently experimentally allowed - if( false == simulation::Paths.insert( path ) ) { - ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); -/* - delete path; - delete pathnode; -*/ - } - simulation::Region->insert_and_register( path ); - } - else if( nodedata.type == "traction" ) { - - auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) }; - // traction loading is optional - if( traction == nullptr ) { return; } - - if( false == simulation::Traction.insert( traction ) ) { - ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } - simulation::Region->insert_and_register( traction ); - } - else if( nodedata.type == "tractionpowersource" ) { - - auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) }; - // traction loading is optional - if( powersource == nullptr ) { return; } - - if( false == simulation::Powergrid.insert( powersource ) ) { - ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } -/* - // TODO: implement this - simulation::Region.insert_powersource( powersource, Scratchpad ); -*/ - } - else if( nodedata.type == "model" ) { - - if( nodedata.range_min < 0.0 ) { - // 3d terrain - if( false == Scratchpad.binary.terrain ) { - // if we're loading data from text .scn file convert and import - auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; - // model import can potentially fail - if( instance == nullptr ) { return; } - // go through submodels, and import them as shapes - auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli - for( auto i = 1; i < cellcount; ++i ) { - auto *submodel = instance->TerrainSquare( i - 1 ); - simulation::Region->insert( - scene::shape_node().convert( submodel ), - Scratchpad, - false ); - // if there's more than one group of triangles in the cell they're held as children of the primary submodel - submodel = submodel->ChildGet(); - while( submodel != nullptr ) { - simulation::Region->insert( - scene::shape_node().convert( submodel ), - Scratchpad, - false ); - submodel = submodel->NextGet(); - } - } - // with the import done we can get rid of the source model - delete instance; - } - else { - // if binary terrain file was present, we already have this data - skip_until( Input, "endmodel" ); - } - } - else { - // regular instance of 3d mesh - auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; - // model import can potentially fail - if( instance == nullptr ) { return; } - - if( false == simulation::Instances.insert( instance ) ) { - ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } - simulation::Region->insert( instance ); - } - } - else if( ( nodedata.type == "triangles" ) - || ( nodedata.type == "triangle_strip" ) - || ( nodedata.type == "triangle_fan" ) ) { - - if( false == Scratchpad.binary.terrain ) { - - simulation::Region->insert( - scene::shape_node().import( - Input, nodedata ), - Scratchpad, - true ); - } - else { - // all shapes were already loaded from the binary version of the file - skip_until( Input, "endtri" ); - } - } - else if( ( nodedata.type == "lines" ) - || ( nodedata.type == "line_strip" ) - || ( nodedata.type == "line_loop" ) ) { - - if( false == Scratchpad.binary.terrain ) { - - simulation::Region->insert( - scene::lines_node().import( - Input, nodedata ), - Scratchpad ); - } - else { - // all lines were already loaded from the binary version of the file - skip_until( Input, "endline" ); - } - } - else if( nodedata.type == "memcell" ) { - - auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) }; - if( false == simulation::Memory.insert( memorycell ) ) { - ErrorLog( "Bad scenario: memory memorycell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } - simulation::Region->insert( memorycell ); - } - else if( nodedata.type == "eventlauncher" ) { - - auto *eventlauncher { deserialize_eventlauncher( Input, Scratchpad, nodedata ) }; - if( false == simulation::Events.insert( eventlauncher ) ) { - ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } - // event launchers can be either global, or local with limited range of activation - // each gets assigned different caretaker - if( true == eventlauncher->IsGlobal() ) { - simulation::Events.queue( eventlauncher ); - } - else { - simulation::Region->insert( eventlauncher ); - } - } - else if( nodedata.type == "sound" ) { - - auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) }; - if( false == simulation::Sounds.insert( sound ) ) { - ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - } - simulation::Region->insert( sound ); - } - -} - -void -state_serializer::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) { - - glm::dvec3 offset; - Input.getTokens( 3 ); - Input - >> offset.x - >> offset.y - >> offset.z; - // sumowanie całkowitego przesunięcia - Scratchpad.location.offset.emplace( - offset + ( - Scratchpad.location.offset.empty() ? - glm::dvec3() : - Scratchpad.location.offset.top() ) ); -} - -void -state_serializer::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) { - - if( false == Scratchpad.location.offset.empty() ) { - Scratchpad.location.offset.pop(); - } - else { - ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); - } -} - -void -state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) { - - Input.getTokens( 3 ); - Input - >> Scratchpad.location.rotation.x - >> Scratchpad.location.rotation.y - >> Scratchpad.location.rotation.z; -} - -void -state_serializer::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) { - - // sky model - Input.getTokens( 1 ); - Input - >> Global.asSky; - // anything else left in the section has no defined meaning - skip_until( Input, "endsky" ); -} - -void -state_serializer::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) { - - // legacy section, no longer supported; - skip_until( Input, "endtest" ); -} - -void -state_serializer::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) { - - // current scenario time - cParser timeparser( Input.getToken() ); - timeparser.getTokens( 2, false, ":" ); - auto &time = simulation::Time.data(); - timeparser - >> time.wHour - >> time.wMinute; - - if( true == Global.ScenarioTimeCurrent ) { - // calculate time shift required to match scenario time with local clock - auto timenow = std::time( 0 ); - auto const *localtime = std::localtime( &timenow ); - Global.ScenarioTimeOffset = ( ( localtime->tm_hour * 60 + localtime->tm_min ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f; - } - - // remaining sunrise and sunset parameters are no longer used, as they're now calculated dynamically - // anything else left in the section has no defined meaning - skip_until( Input, "endtime" ); -} - -void -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 - deserialize_endtrainset( Input, Scratchpad ); - ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" ); - } - - Scratchpad.trainset = scene::scratch_data::trainset_data(); - Scratchpad.trainset.is_open = true; - - Input.getTokens( 4 ); - Input - >> Scratchpad.trainset.name - >> Scratchpad.trainset.track - >> Scratchpad.trainset.offset - >> Scratchpad.trainset.velocity; -} - -void -state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) { - - if( ( false == Scratchpad.trainset.is_open ) - || ( true == Scratchpad.trainset.vehicles.empty() ) ) { - // not bloody likely but we better check for it just the same - ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); - Scratchpad.trainset.is_open = false; - return; - } - - std::size_t vehicleindex { 0 }; - for( auto *vehicle : Scratchpad.trainset.vehicles ) { - // go through list of vehicles in the trainset, coupling them together and checking for potential driver - if( ( vehicle->Mechanik != nullptr ) - && ( vehicle->Mechanik->Primary() ) ) { - // primary driver will receive the timetable for this trainset - Scratchpad.trainset.driver = vehicle; - } - if( vehicleindex > 0 ) { - // from second vehicle on couple it with the previous one - Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev( - vehicle, - Scratchpad.trainset.couplings[ vehicleindex - 1 ] ); - } - ++vehicleindex; - } - - if( Scratchpad.trainset.driver != nullptr ) { - // if present, send timetable to the driver - // wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy - auto *controller = Scratchpad.trainset.driver->Mechanik; - controller->DirectionInitial(); - controller->PutCommand( - "Timetable:" + Scratchpad.trainset.name, - Scratchpad.trainset.velocity, - 0, - nullptr ); - } - if( Scratchpad.trainset.couplings.back() == coupling::faux ) { - // jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi) - Scratchpad.trainset.vehicles.back()->RaLightsSet( -1, light::rearendsignals ); - } - // all done - Scratchpad.trainset.is_open = false; -} - -// creates path and its wrapper, restoring class data from provided stream -TTrack * -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 ); - auto const offset { ( - Scratchpad.location.offset.empty() ? - glm::dvec3 { 0.0 } : - glm::dvec3 { - Scratchpad.location.offset.top().x, - Scratchpad.location.offset.top().y, - Scratchpad.location.offset.top().z } ) }; - track->Load( &Input, offset ); - - return track; -} - -TTraction * -state_serializer::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { - - if( false == Global.bLoadTraction ) { - skip_until( Input, "endtraction" ); - return nullptr; - } - // TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done - auto *traction = new TTraction( Nodedata ); - auto offset = ( - Scratchpad.location.offset.empty() ? - glm::dvec3() : - Scratchpad.location.offset.top() ); - traction->Load( &Input, offset ); - - return traction; -} - -TTractionPowerSource * -state_serializer::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { - - if( false == Global.bLoadTraction ) { - skip_until( Input, "end" ); - return nullptr; - } - - auto *powersource = new TTractionPowerSource( Nodedata ); - powersource->Load( &Input ); - // adjust location - powersource->location( transform( powersource->location(), Scratchpad ) ); - - return powersource; -} - -TMemCell * -state_serializer::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { - - auto *memorycell = new TMemCell( Nodedata ); - memorycell->Load( &Input ); - // adjust location - memorycell->location( transform( memorycell->location(), Scratchpad ) ); - - return memorycell; -} - -TEventLauncher * -state_serializer::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { - - glm::dvec3 location; - Input.getTokens( 3 ); - Input - >> location.x - >> location.y - >> location.z; - - auto *eventlauncher = new TEventLauncher( Nodedata ); - eventlauncher->Load( &Input ); - eventlauncher->location( transform( location, Scratchpad ) ); - - return eventlauncher; -} - -TAnimModel * -state_serializer::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { - - glm::dvec3 location; - glm::vec3 rotation; - Input.getTokens( 4 ); - Input - >> location.x - >> location.y - >> location.z - >> rotation.y; - - auto *instance = new TAnimModel( Nodedata ); - instance->Angles( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii - - if( instance->Load( &Input, false ) ) { - instance->location( transform( location, Scratchpad ) ); - } - else { - // model nie wczytał się - ignorowanie node - SafeDelete( instance ); - } - - return instance; -} - -TDynamicObject * -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 - Scratchpad.trainset = scene::scratch_data::trainset_data(); - } - auto const inputline { Input.Line() }; // cache in case of errors - // basic attributes - auto const datafolder { Input.getToken() }; - auto const skinfile { Input.getToken() }; - auto const mmdfile { Input.getToken() }; - auto const pathname = ( - Scratchpad.trainset.is_open ? - Scratchpad.trainset.track : - Input.getToken() ); - auto const offset { Input.getToken( false ) }; - auto const drivertype { Input.getToken() }; - auto const couplingdata = ( - Scratchpad.trainset.is_open ? - Input.getToken() : - "3" ); - auto const velocity = ( - Scratchpad.trainset.is_open ? - Scratchpad.trainset.velocity : - Input.getToken( false ) ); - // extract coupling type and optional parameters - auto const couplingdatawithparams = couplingdata.find( '.' ); - auto coupling = ( - couplingdatawithparams != std::string::npos ? - std::atoi( couplingdata.substr( 0, couplingdatawithparams ).c_str() ) : - std::atoi( couplingdata.c_str() ) ); - if( coupling < 0 ) { - // sprzęg zablokowany (pojazdy nierozłączalne przy manewrach) - coupling = ( -coupling ) | coupling::permanent; - } - if( ( offset != -1.0 ) - && ( std::abs( offset ) > 0.5 ) ) { // maksymalna odległość między sprzęgami - do przemyślenia - // likwidacja sprzęgu, jeśli odległość zbyt duża - to powinno być uwzględniane w fizyce sprzęgów... - coupling = coupling::faux; - } - auto const params = ( - couplingdatawithparams != std::string::npos ? - couplingdata.substr( couplingdatawithparams + 1 ) : - "" ); - // load amount and type - auto loadcount { Input.getToken( false ) }; - auto loadtype = ( - loadcount ? - Input.getToken() : - "" ); - if( loadtype == "enddynamic" ) { - // idiotoodporność: ładunek bez podanego typu nie liczy się jako ładunek - loadcount = 0; - loadtype = ""; - } - - auto *path = simulation::Paths.find( pathname ); - if( path == nullptr ) { - - ErrorLog( "Bad scenario: vehicle \"" + Nodedata.name + "\" placed on nonexistent path \"" + pathname + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); - skip_until( Input, "enddynamic" ); - return nullptr; - } - - if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd, - && ( false == path->m_events0.empty() ) // tor ma Event0 - && ( std::abs( velocity ) <= 1.f ) // a skład stoi - && ( Scratchpad.trainset.offset >= 0.0 ) // ale może nie sięgać na owy tor - && ( Scratchpad.trainset.offset < 8.0 ) ) { // i raczej nie sięga - // przesuwamy około pół EU07 dla wstecznej zgodności - Scratchpad.trainset.offset = 8.0; - } - - auto *vehicle = new TDynamicObject(); - - auto const length = - vehicle->Init( - Nodedata.name, - datafolder, skinfile, mmdfile, - path, - ( offset == -1.0 ? - Scratchpad.trainset.offset : - Scratchpad.trainset.offset - offset ), - drivertype, - velocity, - Scratchpad.trainset.name, - loadcount, loadtype, - ( offset == -1.0 ), - params ); - - if( length != 0.0 ) { // zero oznacza błąd - // przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1 - Scratchpad.trainset.offset -= length; - // automatically establish permanent connections for couplers which specify them in their definitions - if( ( coupling != 0 ) - && ( vehicle->MoverParameters->Couplers[ ( offset == -1.0 ? side::front : side::rear ) ].AllowedFlag & coupling::permanent ) ) { - coupling |= coupling::permanent; - } - if( true == Scratchpad.trainset.is_open ) { - Scratchpad.trainset.vehicles.emplace_back( vehicle ); - Scratchpad.trainset.couplings.emplace_back( coupling ); - } - } - else { - if( vehicle->MyTrack != nullptr ) { - // rare failure case where vehicle with length of 0 is added to the track, - // treated as error code and consequently deleted, but still remains on the track - vehicle->MyTrack->RemoveDynamicObject( vehicle ); - } - delete vehicle; - skip_until( Input, "enddynamic" ); - return nullptr; - } - - auto const destination { Input.getToken() }; - if( destination != "enddynamic" ) { - // optional vehicle destination parameter - vehicle->asDestination = Input.getToken(); - skip_until( Input, "enddynamic" ); - } - - return vehicle; -} - -sound_source * -state_serializer::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { - - glm::dvec3 location; - Input.getTokens( 3 ); - Input - >> location.x - >> location.y - >> location.z; - // adjust location - location = transform( location, Scratchpad ); - - auto *sound = new sound_source( sound_placement::external, Nodedata.range_max ); - sound->offset( location ); - sound->name( Nodedata.name ); - sound->deserialize( Input, sound_type::single ); - - skip_until( Input, "endsound" ); - - return sound; -} - -// skips content of stream until specified token -void -state_serializer::skip_until( cParser &Input, std::string const &Token ) { - - std::string token { Input.getToken() }; - while( ( false == token.empty() ) - && ( token != Token ) ) { - - token = Input.getToken(); - } -} - -// transforms provided location by specifed rotation and offset -glm::dvec3 -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 ); - Location = glm::rotateY( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji - } - if( false == Scratchpad.location.offset.empty() ) { - Location += Scratchpad.location.offset.top(); - } - return Location; -} - - -// stores class data in specified file, in legacy (text) format -void -state_serializer::export_as_text( std::string const &Scenariofile ) const { - - if( Scenariofile == "$.scn" ) { - ErrorLog( "Bad file: scenery export not supported for file \"$.scn\"" ); - } - else { - WriteLog( "Scenery data export in progress..." ); - } - - auto filename { Scenariofile }; - while( filename[ 0 ] == '$' ) { - // trim leading $ char rainsted utility may add to the base name for modified .scn files - filename.erase( 0, 1 ); - } - erase_extension( filename ); - filename = Global.asCurrentSceneryPath + filename + "_export"; - - std::ofstream scmfile { filename + ".scm" }; - // tracks - scmfile << "// paths\n"; - for( auto const *path : Paths.sequence() ) { - path->export_as_text( scmfile ); - } - // traction - scmfile << "// traction\n"; - for( auto const *traction : Traction.sequence() ) { - traction->export_as_text( scmfile ); - } - // power grid - scmfile << "// traction power sources\n"; - for( auto const *powersource : Powergrid.sequence() ) { - powersource->export_as_text( scmfile ); - } - // models - scmfile << "// instanced models\n"; - for( auto const *instance : Instances.sequence() ) { - instance->export_as_text( scmfile ); - } - // sounds - scmfile << "// sounds\n"; - Region->export_as_text( scmfile ); - - std::ofstream ctrfile { filename + ".ctr" }; - // mem cells - ctrfile << "// memory cells\n"; - for( auto const *memorycell : Memory.sequence() ) { - if( true == memorycell->is_exportable ) { - memorycell->export_as_text( ctrfile ); - } - } - // events - Events.export_as_text( ctrfile ); - - WriteLog( "Scenery data export done." ); } } // simulation diff --git a/simulation.h b/simulation.h index 3e3a35f0..28d76da6 100644 --- a/simulation.h +++ b/simulation.h @@ -9,67 +9,11 @@ http://mozilla.org/MPL/2.0/. #pragma once -#include "parser.h" -#include "scene.h" -#include "event.h" -#include "memcell.h" -#include "evlaunch.h" -#include "track.h" -#include "traction.h" -#include "tractionpower.h" -#include "sound.h" -#include "animmodel.h" -#include "dynobj.h" -#include "driver.h" -#include "lightarray.h" +#include "simulationstateserializer.h" +#include "classes.h" namespace simulation { -class state_serializer { - -public: -// methods - // 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: -// methods - // restores class data from provided stream - void deserialize( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ); - TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - // skips content of stream until specified token - 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: @@ -77,6 +21,8 @@ public: // legacy method, calculates changes in simulation state over specified time void update( double Deltatime, int Iterationcount ); + void + update_clocks(); // restores simulation data from specified file. returns: true on success, false otherwise bool deserialize( std::string const &Scenariofile ); @@ -89,6 +35,9 @@ private: state_serializer m_serializer; }; +// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel +void radio_message( sound_source *Message, int const Channel ); + extern state_manager State; extern event_manager Events; extern memory_table Memory; @@ -101,6 +50,9 @@ extern vehicle_table Vehicles; extern light_array Lights; extern scene::basic_region *Region; +extern TTrain *Train; + +extern bool is_ready; } // simulation diff --git a/simulationenvironment.cpp b/simulationenvironment.cpp new file mode 100644 index 00000000..5cef14eb --- /dev/null +++ b/simulationenvironment.cpp @@ -0,0 +1,158 @@ +/* +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 "simulationenvironment.h" + +#include "globals.h" + +namespace simulation { + +world_environment Environment; + +} // simulation + +void +world_environment::toggle_daylight() { + + Global.FakeLight = !Global.FakeLight; + + if( Global.FakeLight ) { + // for fake daylight enter fixed hour + time( 10, 30, 0 ); + } + else { + // local clock based calculation + time(); + } +} + +// calculates current season of the year based on set simulation date +void +world_environment::compute_season( int const Yearday ) const { + + using dayseasonpair = std::pair; + + std::vector seasonsequence { + { 65, "winter:" }, + { 158, "spring:" }, + { 252, "summer:" }, + { 341, "autumn:" }, + { 366, "winter:" } }; + auto const lookup = + std::lower_bound( + std::begin( seasonsequence ), std::end( seasonsequence ), + clamp( Yearday, 1, seasonsequence.back().first ), + []( dayseasonpair const &Left, const int Right ) { + return Left.first < Right; } ); + + Global.Season = lookup->second; + // season can affect the weather so if it changes, re-calculate weather as well + compute_weather(); +} + +// calculates current weather +void +world_environment::compute_weather() const { + + Global.Weather = ( + Global.Overcast < 0.25 ? "clear:" : + Global.Overcast < 1.0 ? "cloudy:" : + ( Global.Season != "winter:" ? + "rain:" : + "snow:" ) ); +} + +void +world_environment::init() { + + m_sun.init(); + m_moon.init(); + m_stars.init(); + m_clouds.Init(); +} + +void +world_environment::update() { + // move celestial bodies... + m_sun.update(); + m_moon.update(); + // ...determine source of key light and adjust global state accordingly... + // diffuse (sun) intensity goes down after twilight, and reaches minimum 18 degrees below horizon + float twilightfactor = clamp( -m_sun.getAngle(), 0.0f, 18.0f ) / 18.0f; + // NOTE: sun light receives extra padding to prevent moon from kicking in too soon + auto const sunlightlevel = m_sun.getIntensity() + 0.05f * ( 1.f - twilightfactor ); + auto const moonlightlevel = m_moon.getIntensity() * 0.65f; // scaled down by arbitrary factor, it's pretty bright otherwise + float keylightintensity; + glm::vec3 keylightcolor; + if( moonlightlevel > sunlightlevel ) { + // rare situations when the moon is brighter than the sun, typically at night + Global.SunAngle = m_moon.getAngle(); + Global.DayLight.position = m_moon.getDirection(); + Global.DayLight.direction = -1.0f * m_moon.getDirection(); + keylightintensity = moonlightlevel; + // if the moon is up, it overrides the twilight + twilightfactor = 0.0f; + keylightcolor = glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 202.0f / 255.0f ); + } + else { + // regular situation with sun as the key light + Global.SunAngle = m_sun.getAngle(); + Global.DayLight.position = m_sun.getDirection(); + Global.DayLight.direction = -1.0f * m_sun.getDirection(); + keylightintensity = sunlightlevel; + // include 'golden hour' effect in twilight lighting + float const duskfactor = 1.0f - clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f; + keylightcolor = interpolate( + glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), + glm::vec3( 235.0f / 255.0f, 140.0f / 255.0f, 36.0f / 255.0f ), + duskfactor ); + } + // ...update skydome to match the current sun position as well... + m_skydome.SetOvercastFactor( Global.Overcast ); + m_skydome.Update( m_sun.getDirection() ); + // ...retrieve current sky colour and brightness... + auto const skydomecolour = m_skydome.GetAverageColor(); + auto const skydomehsv = colors::RGBtoHSV( skydomecolour ); + // sun strength is reduced by overcast level + keylightintensity *= ( 1.0f - std::min( 1.f, Global.Overcast ) * 0.65f ); + + // intensity combines intensity of the sun and the light reflected by the sky dome + // it'd be more technically correct to have just the intensity of the sun here, + // but whether it'd _look_ better is something to be tested + auto const intensity = std::min( 1.15f * ( 0.05f + keylightintensity + skydomehsv.z ), 1.25f ); + // the impact of sun component is reduced proportionally to overcast level, as overcast increases role of ambient light + auto const diffuselevel = interpolate( keylightintensity, intensity * ( 1.0f - twilightfactor ), 1.0f - std::min( 1.f, Global.Overcast ) * 0.75f ); + // ...update light colours and intensity. + keylightcolor = keylightcolor * diffuselevel; + Global.DayLight.diffuse = glm::vec4( keylightcolor, Global.DayLight.diffuse.a ); + Global.DayLight.specular = glm::vec4( keylightcolor * 0.85f, diffuselevel ); + + // tonal impact of skydome color is inversely proportional to how high the sun is above the horizon + // (this is pure conjecture, aimed more to 'look right' than be accurate) + float const ambienttone = clamp( 1.0f - ( Global.SunAngle / 90.0f ), 0.0f, 1.0f ); + Global.DayLight.ambient[ 0 ] = interpolate( skydomehsv.z, skydomecolour.r, ambienttone ); + Global.DayLight.ambient[ 1 ] = interpolate( skydomehsv.z, skydomecolour.g, ambienttone ); + Global.DayLight.ambient[ 2 ] = interpolate( skydomehsv.z, skydomecolour.b, ambienttone ); + + Global.fLuminance = intensity; + + // update the fog. setting it to match the average colour of the sky dome is cheap + // but quite effective way to make the distant items blend with background better + // NOTE: base brightness calculation provides scaled up value, so we bring it back to 'real' one here + Global.FogColor = m_skydome.GetAverageHorizonColor(); +} + +void +world_environment::time( int const Hour, int const Minute, int const Second ) { + + m_sun.setTime( Hour, Minute, Second ); +} + +//--------------------------------------------------------------------------- diff --git a/simulationenvironment.h b/simulationenvironment.h new file mode 100644 index 00000000..ce3b83a4 --- /dev/null +++ b/simulationenvironment.h @@ -0,0 +1,52 @@ +/* +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 + +#include "sky.h" +#include "sun.h" +#include "moon.h" +#include "stars.h" +#include "skydome.h" + +class opengl_renderer; + +// wrapper for environment elements -- sky, sun, stars, clouds etc +class world_environment { + + friend opengl_renderer; + +public: +// methods + void init(); + void update(); + void time( int const Hour = -1, int const Minute = -1, int const Second = -1 ); + // switches between static and dynamic daylight calculation + void toggle_daylight(); + // calculates current season of the year based on set simulation date + void compute_season( int const Yearday ) const; + // calculates current weather + void compute_weather() const; + +private: +// members + CSkyDome m_skydome; + cStars m_stars; + cSun m_sun; + cMoon m_moon; + TSky m_clouds; +}; + +namespace simulation { + +extern world_environment Environment; + +} // simulation + +//--------------------------------------------------------------------------- diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp new file mode 100644 index 00000000..bd589bc7 --- /dev/null +++ b/simulationstateserializer.cpp @@ -0,0 +1,934 @@ +/* +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 + +#include "stdafx.h" +#include "simulationstateserializer.h" + +#include "globals.h" +#include "simulation.h" +#include "simulationtime.h" +#include "event.h" +#include "driver.h" +#include "dynobj.h" +#include "animmodel.h" +#include "tractionpower.h" +#include "application.h" +#include "renderer.h" +#include "logs.h" + +namespace simulation { + +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(); + + // TODO: check first for presence of serialized binary files + // if this fails, fall back on the legacy text format + scene::scratch_data importscratchpad; + if( Scenariofile != "$.scn" ) { + // compilation to binary file isn't supported for rainsted-created overrides + importscratchpad.binary.terrain = Region->deserialize( Scenariofile ); + } + // NOTE: for the time being import from text format is a given, since we don't have full binary serialization + cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction ); + + if( false == scenarioparser.ok() ) { return false; } + + deserialize( scenarioparser, importscratchpad ); + if( ( false == importscratchpad.binary.terrain ) + && ( Scenariofile != "$.scn" ) ) { + // if we didn't find usable binary version of the scenario files, create them now for future use + // as long as the scenario file wasn't rainsted-created base file override + Region->serialize( Scenariofile ); + } + return true; +} + +// restores class data from provided stream +void +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_serializer::*)(cParser &, scene::scratch_data &); + std::vector< + std::pair< + std::string, + deserializefunction> > functionlist = { + { "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, + deserializefunctionbind> functionmap; + for( auto &function : functionlist ) { + functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) ); + } + + // deserialize content from the provided input + auto + timelast { std::chrono::steady_clock::now() }, + timenow { timelast }; + std::string token { Input.getToken() }; + while( false == token.empty() ) { + + auto lookup = functionmap.find( token ); + if( lookup != functionmap.end() ) { + lookup->second(); + } + else { + ErrorLog( "Bad scenario: unexpected token \"" + token + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); + } + + timenow = std::chrono::steady_clock::now(); + if( std::chrono::duration_cast( timenow - timelast ).count() >= 200 ) { + timelast = timenow; + glfwPollEvents(); + Application.set_progress( Input.getProgress(), Input.getFullProgress() ); + GfxRenderer.Render(); + } + + token = Input.getToken(); + } + + if( false == Scratchpad.initialized ) { + // manually perform scenario initialization + deserialize_firstinit( Input, Scratchpad ); + } +} + +void +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 + Input.getTokens( 3 ); + // fog range + Input.getTokens( 2 ); + Input + >> Global.fFogStart + >> Global.fFogEnd; + + if( Global.fFogEnd > 0.0 ) { + // fog colour; optional legacy parameter, no longer used + Input.getTokens( 3 ); + } + + std::string token { Input.getToken() }; + if( token != "endatmo" ) { + // optional overcast parameter + Global.Overcast = clamp( std::stof( token ), 0.f, 2.f ); + // overcast drives weather so do a calculation here + // NOTE: ugly, clean it up when we're done with world refactoring + simulation::Environment.compute_weather(); + } + while( ( false == token.empty() ) + && ( token != "endatmo" ) ) { + // anything else left in the section has no defined meaning + token = Input.getToken(); + } +} + +void +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ć + std::string token; + do { // opcjonalna siódma liczba określa numer kamery, a kiedyś były tylko 3 + Input.getTokens(); + Input >> token; + switch( ++i ) { // kiedyś camera miało tylko 3 współrzędne + case 0: { xyz.x = atof( token.c_str() ); break; } + case 1: { xyz.y = atof( token.c_str() ); break; } + case 2: { xyz.z = atof( token.c_str() ); break; } + case 3: { abc.x = atof( token.c_str() ); break; } + case 4: { abc.y = atof( token.c_str() ); break; } + case 5: { abc.z = atof( token.c_str() ); break; } + case 6: { into = atoi( token.c_str() ); break; } // takie sobie, bo można wpisać -1 + default: { break; } + } + } while( token.compare( "endcamera" ) != 0 ); + if( into < 0 ) + into = ++Global.iCameraLast; + if( into < 10 ) { // przepisanie do odpowiedniego miejsca w tabelce + Global.FreeCameraInit[ into ] = xyz; + Global.FreeCameraInitAngle[ into ] = + Math3D::vector3( + glm::radians( abc.x ), + glm::radians( abc.y ), + glm::radians( abc.z ) ); + Global.iCameraLast = into; // numer ostatniej + } +/* + // cleaned up version of the above. + // NOTE: no longer supports legacy mode where some parameters were optional + Input.getTokens( 7 ); + glm::vec3 + position, + rotation; + int index; + Input + >> position.x + >> position.y + >> position.z + >> rotation.x + >> rotation.y + >> rotation.z + >> index; + + skip_until( Input, "endcamera" ); + + // TODO: finish this +*/ +} + +void +state_serializer::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) { + + // config parameters (re)definition + Global.ConfigParse( Input ); +} + +void +state_serializer::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) { + + // legacy section, never really used; + skip_until( Input, "enddescription" ); +} + +void +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(); + Math3D::vector3 offset = ( + Scratchpad.location.offset.empty() ? + Math3D::vector3() : + Math3D::vector3( + Scratchpad.location.offset.top().x, + Scratchpad.location.offset.top().y, + Scratchpad.location.offset.top().z ) ); + event->Load( &Input, offset ); + + if( false == simulation::Events.insert( event ) ) { + delete event; + } +} + +void +state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { + + if( true == Scratchpad.initialized ) { return; } + + simulation::Paths.InitTracks(); + simulation::Traction.InitTraction(); + simulation::Events.InitEvents(); + simulation::Events.InitLaunchers(); + simulation::Memory.InitCells(); + + Scratchpad.initialized = true; +} + +void +state_serializer::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) { + + // legacy section, no longer used nor supported; + skip_until( Input, "endlight" ); +} + +void +state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) { + + auto const inputline = Input.Line(); // cache in case we need to report error + + scene::node_data nodedata; + // common data and node type indicator + Input.getTokens( 4 ); + Input + >> nodedata.range_max + >> nodedata.range_min + >> nodedata.name + >> nodedata.type; + // type-based deserialization. not elegant but it'll do + if( nodedata.type == "dynamic" ) { + + auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) }; + // vehicle import can potentially fail + if( vehicle == nullptr ) { return; } + + if( false == simulation::Vehicles.insert( vehicle ) ) { + + ErrorLog( "Bad scenario: vehicle with duplicate name \"" + vehicle->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + + if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only + && ( ( ( vehicle->LightList( side::front ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) + || ( ( vehicle->LightList( side::rear ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) ) ) { + simulation::Lights.insert( vehicle ); + } + } + else if( nodedata.type == "track" ) { + + auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; + // duplicates of named tracks are currently experimentally allowed + if( false == simulation::Paths.insert( path ) ) { + ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); +/* + delete path; + delete pathnode; +*/ + } + simulation::Region->insert_and_register( path ); + } + else if( nodedata.type == "traction" ) { + + auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) }; + // traction loading is optional + if( traction == nullptr ) { return; } + + if( false == simulation::Traction.insert( traction ) ) { + ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + simulation::Region->insert_and_register( traction ); + } + else if( nodedata.type == "tractionpowersource" ) { + + auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) }; + // traction loading is optional + if( powersource == nullptr ) { return; } + + if( false == simulation::Powergrid.insert( powersource ) ) { + ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } +/* + // TODO: implement this + simulation::Region.insert_powersource( powersource, Scratchpad ); +*/ + } + else if( nodedata.type == "model" ) { + + if( nodedata.range_min < 0.0 ) { + // 3d terrain + if( false == Scratchpad.binary.terrain ) { + // if we're loading data from text .scn file convert and import + auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; + // model import can potentially fail + if( instance == nullptr ) { return; } + // go through submodels, and import them as shapes + auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli + for( auto i = 1; i < cellcount; ++i ) { + auto *submodel = instance->TerrainSquare( i - 1 ); + simulation::Region->insert( + scene::shape_node().convert( submodel ), + Scratchpad, + false ); + // if there's more than one group of triangles in the cell they're held as children of the primary submodel + submodel = submodel->ChildGet(); + while( submodel != nullptr ) { + simulation::Region->insert( + scene::shape_node().convert( submodel ), + Scratchpad, + false ); + submodel = submodel->NextGet(); + } + } + // with the import done we can get rid of the source model + delete instance; + } + else { + // if binary terrain file was present, we already have this data + skip_until( Input, "endmodel" ); + } + } + else { + // regular instance of 3d mesh + auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; + // model import can potentially fail + if( instance == nullptr ) { return; } + + if( false == simulation::Instances.insert( instance ) ) { + ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + simulation::Region->insert( instance ); + } + } + else if( ( nodedata.type == "triangles" ) + || ( nodedata.type == "triangle_strip" ) + || ( nodedata.type == "triangle_fan" ) ) { + + if( false == Scratchpad.binary.terrain ) { + + simulation::Region->insert( + scene::shape_node().import( + Input, nodedata ), + Scratchpad, + true ); + } + else { + // all shapes were already loaded from the binary version of the file + skip_until( Input, "endtri" ); + } + } + else if( ( nodedata.type == "lines" ) + || ( nodedata.type == "line_strip" ) + || ( nodedata.type == "line_loop" ) ) { + + if( false == Scratchpad.binary.terrain ) { + + simulation::Region->insert( + scene::lines_node().import( + Input, nodedata ), + Scratchpad ); + } + else { + // all lines were already loaded from the binary version of the file + skip_until( Input, "endline" ); + } + } + else if( nodedata.type == "memcell" ) { + + auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) }; + if( false == simulation::Memory.insert( memorycell ) ) { + ErrorLog( "Bad scenario: memory memorycell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + simulation::Region->insert( memorycell ); + } + else if( nodedata.type == "eventlauncher" ) { + + auto *eventlauncher { deserialize_eventlauncher( Input, Scratchpad, nodedata ) }; + if( false == simulation::Events.insert( eventlauncher ) ) { + ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + // event launchers can be either global, or local with limited range of activation + // each gets assigned different caretaker + if( true == eventlauncher->IsGlobal() ) { + simulation::Events.queue( eventlauncher ); + } + else { + simulation::Region->insert( eventlauncher ); + } + } + else if( nodedata.type == "sound" ) { + + auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) }; + if( false == simulation::Sounds.insert( sound ) ) { + ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + simulation::Region->insert( sound ); + } + +} + +void +state_serializer::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) { + + glm::dvec3 offset; + Input.getTokens( 3 ); + Input + >> offset.x + >> offset.y + >> offset.z; + // sumowanie całkowitego przesunięcia + Scratchpad.location.offset.emplace( + offset + ( + Scratchpad.location.offset.empty() ? + glm::dvec3() : + Scratchpad.location.offset.top() ) ); +} + +void +state_serializer::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) { + + if( false == Scratchpad.location.offset.empty() ) { + Scratchpad.location.offset.pop(); + } + else { + ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); + } +} + +void +state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) { + + Input.getTokens( 3 ); + Input + >> Scratchpad.location.rotation.x + >> Scratchpad.location.rotation.y + >> Scratchpad.location.rotation.z; +} + +void +state_serializer::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) { + + // sky model + Input.getTokens( 1 ); + Input + >> Global.asSky; + // anything else left in the section has no defined meaning + skip_until( Input, "endsky" ); +} + +void +state_serializer::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) { + + // legacy section, no longer supported; + skip_until( Input, "endtest" ); +} + +void +state_serializer::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) { + + // current scenario time + cParser timeparser( Input.getToken() ); + timeparser.getTokens( 2, false, ":" ); + auto &time = simulation::Time.data(); + timeparser + >> time.wHour + >> time.wMinute; + + if( true == Global.ScenarioTimeCurrent ) { + // calculate time shift required to match scenario time with local clock + auto timenow = std::time( 0 ); + auto const *localtime = std::localtime( &timenow ); + Global.ScenarioTimeOffset = ( ( localtime->tm_hour * 60 + localtime->tm_min ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f; + } + + // remaining sunrise and sunset parameters are no longer used, as they're now calculated dynamically + // anything else left in the section has no defined meaning + skip_until( Input, "endtime" ); +} + +void +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 + deserialize_endtrainset( Input, Scratchpad ); + ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" ); + } + + Scratchpad.trainset = scene::scratch_data::trainset_data(); + Scratchpad.trainset.is_open = true; + + Input.getTokens( 4 ); + Input + >> Scratchpad.trainset.name + >> Scratchpad.trainset.track + >> Scratchpad.trainset.offset + >> Scratchpad.trainset.velocity; +} + +void +state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) { + + if( ( false == Scratchpad.trainset.is_open ) + || ( true == Scratchpad.trainset.vehicles.empty() ) ) { + // not bloody likely but we better check for it just the same + ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); + Scratchpad.trainset.is_open = false; + return; + } + + std::size_t vehicleindex { 0 }; + for( auto *vehicle : Scratchpad.trainset.vehicles ) { + // go through list of vehicles in the trainset, coupling them together and checking for potential driver + if( ( vehicle->Mechanik != nullptr ) + && ( vehicle->Mechanik->Primary() ) ) { + // primary driver will receive the timetable for this trainset + Scratchpad.trainset.driver = vehicle; + } + if( vehicleindex > 0 ) { + // from second vehicle on couple it with the previous one + Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev( + vehicle, + Scratchpad.trainset.couplings[ vehicleindex - 1 ] ); + } + ++vehicleindex; + } + + if( Scratchpad.trainset.driver != nullptr ) { + // if present, send timetable to the driver + // wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy + auto *controller = Scratchpad.trainset.driver->Mechanik; + controller->DirectionInitial(); + controller->PutCommand( + "Timetable:" + Scratchpad.trainset.name, + Scratchpad.trainset.velocity, + 0, + nullptr ); + } + if( Scratchpad.trainset.couplings.back() == coupling::faux ) { + // jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi) + Scratchpad.trainset.vehicles.back()->RaLightsSet( -1, light::rearendsignals ); + } + // all done + Scratchpad.trainset.is_open = false; +} + +// creates path and its wrapper, restoring class data from provided stream +TTrack * +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 ); + auto const offset { ( + Scratchpad.location.offset.empty() ? + glm::dvec3 { 0.0 } : + glm::dvec3 { + Scratchpad.location.offset.top().x, + Scratchpad.location.offset.top().y, + Scratchpad.location.offset.top().z } ) }; + track->Load( &Input, offset ); + + return track; +} + +TTraction * +state_serializer::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + if( false == Global.bLoadTraction ) { + skip_until( Input, "endtraction" ); + return nullptr; + } + // TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done + auto *traction = new TTraction( Nodedata ); + auto offset = ( + Scratchpad.location.offset.empty() ? + glm::dvec3() : + Scratchpad.location.offset.top() ); + traction->Load( &Input, offset ); + + return traction; +} + +TTractionPowerSource * +state_serializer::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + if( false == Global.bLoadTraction ) { + skip_until( Input, "end" ); + return nullptr; + } + + auto *powersource = new TTractionPowerSource( Nodedata ); + powersource->Load( &Input ); + // adjust location + powersource->location( transform( powersource->location(), Scratchpad ) ); + + return powersource; +} + +TMemCell * +state_serializer::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + auto *memorycell = new TMemCell( Nodedata ); + memorycell->Load( &Input ); + // adjust location + memorycell->location( transform( memorycell->location(), Scratchpad ) ); + + return memorycell; +} + +TEventLauncher * +state_serializer::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + glm::dvec3 location; + Input.getTokens( 3 ); + Input + >> location.x + >> location.y + >> location.z; + + auto *eventlauncher = new TEventLauncher( Nodedata ); + eventlauncher->Load( &Input ); + eventlauncher->location( transform( location, Scratchpad ) ); + + return eventlauncher; +} + +TAnimModel * +state_serializer::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + glm::dvec3 location; + glm::vec3 rotation; + Input.getTokens( 4 ); + Input + >> location.x + >> location.y + >> location.z + >> rotation.y; + + auto *instance = new TAnimModel( Nodedata ); + instance->Angles( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii + + if( instance->Load( &Input, false ) ) { + instance->location( transform( location, Scratchpad ) ); + } + else { + // model nie wczytał się - ignorowanie node + SafeDelete( instance ); + } + + return instance; +} + +TDynamicObject * +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 + Scratchpad.trainset = scene::scratch_data::trainset_data(); + } + auto const inputline { Input.Line() }; // cache in case of errors + // basic attributes + auto const datafolder { Input.getToken() }; + auto const skinfile { Input.getToken() }; + auto const mmdfile { Input.getToken() }; + auto const pathname = ( + Scratchpad.trainset.is_open ? + Scratchpad.trainset.track : + Input.getToken() ); + auto const offset { Input.getToken( false ) }; + auto const drivertype { Input.getToken() }; + auto const couplingdata = ( + Scratchpad.trainset.is_open ? + Input.getToken() : + "3" ); + auto const velocity = ( + Scratchpad.trainset.is_open ? + Scratchpad.trainset.velocity : + Input.getToken( false ) ); + // extract coupling type and optional parameters + auto const couplingdatawithparams = couplingdata.find( '.' ); + auto coupling = ( + couplingdatawithparams != std::string::npos ? + std::atoi( couplingdata.substr( 0, couplingdatawithparams ).c_str() ) : + std::atoi( couplingdata.c_str() ) ); + if( coupling < 0 ) { + // sprzęg zablokowany (pojazdy nierozłączalne przy manewrach) + coupling = ( -coupling ) | coupling::permanent; + } + if( ( offset != -1.0 ) + && ( std::abs( offset ) > 0.5 ) ) { // maksymalna odległość między sprzęgami - do przemyślenia + // likwidacja sprzęgu, jeśli odległość zbyt duża - to powinno być uwzględniane w fizyce sprzęgów... + coupling = coupling::faux; + } + auto const params = ( + couplingdatawithparams != std::string::npos ? + couplingdata.substr( couplingdatawithparams + 1 ) : + "" ); + // load amount and type + auto loadcount { Input.getToken( false ) }; + auto loadtype = ( + loadcount ? + Input.getToken() : + "" ); + if( loadtype == "enddynamic" ) { + // idiotoodporność: ładunek bez podanego typu nie liczy się jako ładunek + loadcount = 0; + loadtype = ""; + } + + auto *path = simulation::Paths.find( pathname ); + if( path == nullptr ) { + + ErrorLog( "Bad scenario: vehicle \"" + Nodedata.name + "\" placed on nonexistent path \"" + pathname + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + skip_until( Input, "enddynamic" ); + return nullptr; + } + + if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd, + && ( false == path->m_events0.empty() ) // tor ma Event0 + && ( std::abs( velocity ) <= 1.f ) // a skład stoi + && ( Scratchpad.trainset.offset >= 0.0 ) // ale może nie sięgać na owy tor + && ( Scratchpad.trainset.offset < 8.0 ) ) { // i raczej nie sięga + // przesuwamy około pół EU07 dla wstecznej zgodności + Scratchpad.trainset.offset = 8.0; + } + + auto *vehicle = new TDynamicObject(); + + auto const length = + vehicle->Init( + Nodedata.name, + datafolder, skinfile, mmdfile, + path, + ( offset == -1.0 ? + Scratchpad.trainset.offset : + Scratchpad.trainset.offset - offset ), + drivertype, + velocity, + Scratchpad.trainset.name, + loadcount, loadtype, + ( offset == -1.0 ), + params ); + + if( length != 0.0 ) { // zero oznacza błąd + // przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1 + Scratchpad.trainset.offset -= length; + // automatically establish permanent connections for couplers which specify them in their definitions + if( ( coupling != 0 ) + && ( vehicle->MoverParameters->Couplers[ ( offset == -1.0 ? side::front : side::rear ) ].AllowedFlag & coupling::permanent ) ) { + coupling |= coupling::permanent; + } + if( true == Scratchpad.trainset.is_open ) { + Scratchpad.trainset.vehicles.emplace_back( vehicle ); + Scratchpad.trainset.couplings.emplace_back( coupling ); + } + } + else { + if( vehicle->MyTrack != nullptr ) { + // rare failure case where vehicle with length of 0 is added to the track, + // treated as error code and consequently deleted, but still remains on the track + vehicle->MyTrack->RemoveDynamicObject( vehicle ); + } + delete vehicle; + skip_until( Input, "enddynamic" ); + return nullptr; + } + + auto const destination { Input.getToken() }; + if( destination != "enddynamic" ) { + // optional vehicle destination parameter + vehicle->asDestination = Input.getToken(); + skip_until( Input, "enddynamic" ); + } + + return vehicle; +} + +sound_source * +state_serializer::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + glm::dvec3 location; + Input.getTokens( 3 ); + Input + >> location.x + >> location.y + >> location.z; + // adjust location + location = transform( location, Scratchpad ); + + auto *sound = new sound_source( sound_placement::external, Nodedata.range_max ); + sound->offset( location ); + sound->name( Nodedata.name ); + sound->deserialize( Input, sound_type::single ); + + skip_until( Input, "endsound" ); + + return sound; +} + +// skips content of stream until specified token +void +state_serializer::skip_until( cParser &Input, std::string const &Token ) { + + std::string token { Input.getToken() }; + while( ( false == token.empty() ) + && ( token != Token ) ) { + + token = Input.getToken(); + } +} + +// transforms provided location by specifed rotation and offset +glm::dvec3 +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 ); + Location = glm::rotateY( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji + } + if( false == Scratchpad.location.offset.empty() ) { + Location += Scratchpad.location.offset.top(); + } + return Location; +} + + +// stores class data in specified file, in legacy (text) format +void +state_serializer::export_as_text( std::string const &Scenariofile ) const { + + if( Scenariofile == "$.scn" ) { + ErrorLog( "Bad file: scenery export not supported for file \"$.scn\"" ); + } + else { + WriteLog( "Scenery data export in progress..." ); + } + + auto filename { Scenariofile }; + while( filename[ 0 ] == '$' ) { + // trim leading $ char rainsted utility may add to the base name for modified .scn files + filename.erase( 0, 1 ); + } + erase_extension( filename ); + filename = Global.asCurrentSceneryPath + filename + "_export"; + + std::ofstream scmfile { filename + ".scm" }; + // tracks + scmfile << "// paths\n"; + for( auto const *path : Paths.sequence() ) { + path->export_as_text( scmfile ); + } + // traction + scmfile << "// traction\n"; + for( auto const *traction : Traction.sequence() ) { + traction->export_as_text( scmfile ); + } + // power grid + scmfile << "// traction power sources\n"; + for( auto const *powersource : Powergrid.sequence() ) { + powersource->export_as_text( scmfile ); + } + // models + scmfile << "// instanced models\n"; + for( auto const *instance : Instances.sequence() ) { + instance->export_as_text( scmfile ); + } + // sounds + scmfile << "// sounds\n"; + Region->export_as_text( scmfile ); + + std::ofstream ctrfile { filename + ".ctr" }; + // mem cells + ctrfile << "// memory cells\n"; + for( auto const *memorycell : Memory.sequence() ) { + if( true == memorycell->is_exportable ) { + memorycell->export_as_text( ctrfile ); + } + } + // events + Events.export_as_text( ctrfile ); + + WriteLog( "Scenery data export done." ); +} + +} // simulation + + //--------------------------------------------------------------------------- diff --git a/simulationstateserializer.h b/simulationstateserializer.h new file mode 100644 index 00000000..306d81b2 --- /dev/null +++ b/simulationstateserializer.h @@ -0,0 +1,64 @@ +/* +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 + +#include "parser.h" +#include "scene.h" + +namespace simulation { + +class state_serializer { + +public: +// methods + // 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: +// methods + // restores class data from provided stream + void deserialize( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ); + TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + // skips content of stream until specified token + 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 ); +}; + +} // simulation + +//--------------------------------------------------------------------------- diff --git a/sound.cpp b/sound.cpp index a4f1a6c8..10a95dbb 100644 --- a/sound.cpp +++ b/sound.cpp @@ -12,9 +12,9 @@ http://mozilla.org/MPL/2.0/. #include "sound.h" #include "parser.h" #include "globals.h" -#include "world.h" #include "train.h" #include "dynobj.h" +#include "simulation.h" // constructors sound_source::sound_source( sound_placement const Placement, float const Range ) : @@ -910,15 +910,15 @@ sound_source::update_soundproofing() { int const activecab = ( Global.CabWindowOpen ? 2 : FreeFlyModeFlag ? 0 : - ( Global.pWorld->train() ? - Global.pWorld->train()->Dynamic()->MoverParameters->ActiveCab : + ( simulation::Train ? + simulation::Train->Occupied()->ActiveCab : 0 ) ); // location-based gain factor: std::uintptr_t soundproofingstamp = reinterpret_cast( ( FreeFlyModeFlag ? nullptr : - ( Global.pWorld->train() ? - Global.pWorld->train()->Dynamic() : + ( simulation::Train ? + simulation::Train->Dynamic() : nullptr ) ) ) + activecab; @@ -941,7 +941,7 @@ sound_source::update_soundproofing() { m_properties.soundproofing = ( soundproofingstamp == 0 ? EU07_SOUNDPROOFING_STRONG : // listener outside HACK: won't be true if active vehicle has open window - ( Global.pWorld->train()->Dynamic() != m_owner ? + ( simulation::Train->Dynamic() != m_owner ? EU07_SOUNDPROOFING_STRONG : // in another vehicle ( activecab == 0 ? EU07_SOUNDPROOFING_STRONG : // listener in the engine compartment @@ -952,7 +952,7 @@ sound_source::update_soundproofing() { m_properties.soundproofing = ( ( ( soundproofingstamp == 0 ) || ( true == Global.CabWindowOpen ) ) ? EU07_SOUNDPROOFING_SOME : // listener outside or has a window open - ( Global.pWorld->train()->Dynamic() != m_owner ? + ( simulation::Train->Dynamic() != m_owner ? EU07_SOUNDPROOFING_STRONG : // in another vehicle ( activecab == 0 ? EU07_SOUNDPROOFING_NONE : // listener in the engine compartment diff --git a/uart.cpp b/uart.cpp index eaeada83..c617b1fc 100644 --- a/uart.cpp +++ b/uart.cpp @@ -2,7 +2,7 @@ #include "uart.h" #include "Globals.h" -#include "World.h" +#include "simulation.h" #include "Train.h" #include "parser.h" #include "Logs.h" @@ -130,7 +130,7 @@ void uart_input::poll() return; last_update = now; - auto *t = Global.pWorld->train(); + auto const *t =simulation::Train; if (!t) return; diff --git a/uilayer.cpp b/uilayer.cpp index 867a74ea..cefc1988 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -1,4 +1,11 @@ - +/* +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 "uilayer.h" @@ -7,20 +14,30 @@ #include "translation.h" #include "simulation.h" #include "simulationtime.h" +#include "event.h" +#include "camera.h" #include "mtable.h" #include "train.h" +#include "driver.h" #include "sceneeditor.h" +#include "animmodel.h" +#include "dynobj.h" +#include "model3d.h" #include "renderer.h" #include "utilities.h" #include "logs.h" -ui_layer UILayer; - extern "C" { GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window ); //m7todo: potrzebne do directsound } +GLFWwindow * ui_layer::m_window { nullptr }; +GLint ui_layer::m_textureunit { GL_TEXTURE0 }; +GLuint ui_layer::m_fontbase { (GLuint)-1 }; // numer DL dla znaków w napisach +bool ui_layer::m_cursorvisible { true }; + + ui_layer::~ui_layer() { /* // this should be invoked manually, or we risk trying to delete the lists after the context is gone @@ -32,21 +49,6 @@ ui_layer::~ui_layer() { bool ui_layer::init( GLFWwindow *Window ) { - clear_texts(); - - UIHeader = std::make_shared( 20, 20 ); // header ui panel - UITable = std::make_shared( 20, 100 ); // schedule or scan table - UITranscripts = std::make_shared( 85, 600 ); // voice transcripts - // make 4 empty lines for the ui header, to cut down on work down the road - UIHeader->text_lines.emplace_back( "", Global.UITextColor ); - UIHeader->text_lines.emplace_back( "", Global.UITextColor ); - UIHeader->text_lines.emplace_back( "", Global.UITextColor ); - UIHeader->text_lines.emplace_back( "", Global.UITextColor ); - // bind the panels with ui object. maybe not the best place for this but, eh - push_back( UIHeader ); - push_back( UITable ); - push_back( UITranscripts ); - m_window = Window; HFONT font; // Windows Font ID m_fontbase = ::glGenLists(96); // storage for 96 characters @@ -82,1007 +84,12 @@ ui_layer::init( GLFWwindow *Window ) { } } -// potentially processes provided input key. returns: true if key was processed, false otherwise bool ui_layer::on_key( int const Key, int const Action ) { - // TODO: pass the input first through an active ui element if there's any - // if the ui element shows no interest or we don't have one, try to interpret the input yourself: - // shared conditions - switch( Key ) { - - case GLFW_KEY_F1: - case GLFW_KEY_F2: - case GLFW_KEY_F3: - case GLFW_KEY_F8: - case GLFW_KEY_F9: - case GLFW_KEY_F10: - case GLFW_KEY_F11: - case GLFW_KEY_F12: { // ui mode selectors - - if( ( true == Global.ctrlState ) - || ( true == Global.shiftState ) ) { - // only react to keys without modifiers - return false; - } - - if( Action == GLFW_RELEASE ) { return true; } // recognized, but ignored - - EditorModeFlag = ( Key == GLFW_KEY_F11 ); - if( ( true == EditorModeFlag ) - && ( false == Global.ControlPicking ) ) { - set_cursor( GLFW_CURSOR_NORMAL ); - Global.ControlPicking = true; - } - } - - default: { // everything else - break; - } - } - - switch (Key) { - - case GLFW_KEY_F1: { - // basic consist info - if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; } - if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) { - // wyłączenie napisów - Global.iTextMode = 0; - Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0; - } - else { - Global.iTextMode = Key; - } - return true; - } - - case GLFW_KEY_F2: { - // parametry pojazdu - if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; } - if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) { - // wyłączenie napisów - Global.iTextMode = 0; - Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0; - } - else { - Global.iTextMode = Key; - } - return true; - } - - case GLFW_KEY_F3: { - // timetable - if( Global.iTextMode == Key ) { ++Global.iScreenMode[ Key - GLFW_KEY_F1 ]; } - if( Global.iScreenMode[ Key - GLFW_KEY_F1 ] > 1 ) { - // wyłączenie napisów - Global.iTextMode = 0; - Global.iScreenMode[ Key - GLFW_KEY_F1 ] = 0; - } - else { - Global.iTextMode = Key; - } - return true; - } - - case GLFW_KEY_F8: { - // renderer debug data - Global.iTextMode = Key; - return true; - } - - case GLFW_KEY_F9: { - // wersja - Global.iTextMode = Key; - return true; - } - - case GLFW_KEY_F10: { - // quit - if( Global.iTextMode == Key ) { - Global.iTextMode = - ( Global.iPause && ( Key != GLFW_KEY_F1 ) ? - GLFW_KEY_F1 : - 0 ); // wyłączenie napisów, chyba że pauza - } - else { - Global.iTextMode = Key; - } - return true; - } - - case GLFW_KEY_F11: { - // scenario inspector - Global.iTextMode = Key; - return true; - } - - case GLFW_KEY_F12: { - // coś tam jeszcze - Global.iTextMode = Key; - return true; - } - - case GLFW_KEY_Y: { - // potentially quit - if( Global.iTextMode != GLFW_KEY_F10 ) { return false; } // not in quit mode - - if( Action == GLFW_RELEASE ) { return true; } // recognized, but ignored - - glfwSetWindowShouldClose( m_window, 1 ); - return true; - } - - default: { - break; - } - } return false; } -// updates state of UI elements -void -ui_layer::update() { - - UITable->text_lines.clear(); - std::string uitextline1, uitextline2, uitextline3, uitextline4; - UILayer.set_tooltip( "" ); - - auto const *train { ( Global.pWorld ? Global.pWorld->train() : nullptr ) }; - auto const *controlled { ( Global.pWorld ? Global.pWorld->controlled() : nullptr ) }; - auto const *camera { Global.pCamera }; - - if( ( train != nullptr ) && ( false == FreeFlyModeFlag ) ) { - if( false == DebugModeFlag ) { - // in regular mode show control functions, for defined controls - UILayer.set_tooltip( locale::label_cab_control( train->GetLabel( GfxRenderer.Pick_Control() ) ) ); - } - else { - // in debug mode show names of submodels, to help with cab setup and/or debugging - auto const cabcontrol = GfxRenderer.Pick_Control(); - UILayer.set_tooltip( ( cabcontrol ? cabcontrol->pName : "" ) ); - } - } - if( ( true == Global.ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) { - auto const scenerynode = GfxRenderer.Pick_Node(); - UILayer.set_tooltip( - ( scenerynode ? - scenerynode->name() : - "" ) ); - } - - switch( Global.iTextMode ) { - - case( GLFW_KEY_F1 ) : { - // f1, default mode: current time and timetable excerpt - auto const &time = simulation::Time.data(); - uitextline1 = - "Time: " - + to_string( time.wHour ) + ":" - + ( time.wMinute < 10 ? "0" : "" ) + to_string( time.wMinute ) + ":" - + ( time.wSecond < 10 ? "0" : "" ) + to_string( time.wSecond ); - if( Global.iPause ) { - uitextline1 += " (paused)"; - } - - if( ( controlled != nullptr ) - && ( controlled->Mechanik != nullptr ) ) { - - auto const &mover = controlled->MoverParameters; - auto const &driver = controlled->Mechanik; - - uitextline2 = "Throttle: " + to_string( driver->Controlling()->MainCtrlPos, 0, 2 ) + "+" + std::to_string( driver->Controlling()->ScndCtrlPos ); - if( mover->ActiveDir > 0 ) { uitextline2 += " D"; } - else if( mover->ActiveDir < 0 ) { uitextline2 += " R"; } - else { uitextline2 += " N"; } - - uitextline3 = "Brakes:" + to_string( mover->fBrakeCtrlPos, 1, 5 ) + "+" + to_string( mover->LocalBrakePosA * LocalBrakePosNo, 0 ) + ( mover->SlippingWheels ? " !" : " " ); - - uitextline4 = ( - true == TestFlag( mover->SecuritySystem.Status, s_aware ) ? - "!ALERTER! " : - " " ); - uitextline4 += ( - true == TestFlag( mover->SecuritySystem.Status, s_active ) ? - "!SHP! " : - " " ); - - if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) { - // detail mode on second key press - auto const speedlimit { static_cast( std::floor( driver->VelDesired ) ) }; - uitextline2 += - " Speed: " + std::to_string( static_cast( std::floor( mover->Vel ) ) ) + " km/h" - + " (limit: " + std::to_string( speedlimit ) + " km/h"; - auto const nextspeedlimit { static_cast( std::floor( driver->VelNext ) ) }; - if( nextspeedlimit != speedlimit ) { - uitextline2 += - ", new limit: " + std::to_string( nextspeedlimit ) + " km/h" - + " in " + to_string( driver->ActualProximityDist * 0.001, 1 ) + " km"; - } - uitextline2 += ")"; - auto const reverser { ( mover->ActiveDir > 0 ? 1 : -1 ) }; - auto const grade { controlled->VectorFront().y * 100 * ( controlled->DirectionGet() == reverser ? 1 : -1 ) * reverser }; - if( std::abs( grade ) >= 0.25 ) { - uitextline2 += " Grade: " + to_string( grade, 1 ) + "%"; - } - uitextline3 += - " Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa" - + " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)"; - - auto const stoptime { static_cast( -1.0 * controlled->Mechanik->fStopTime ) }; - if( stoptime > 0 ) { - uitextline4 += " Loading/unloading in progress (" + to_string( stoptime ) + ( stoptime > 1 ? " seconds" : " second" ) + " left)"; - } - else { - auto const trackblockdistance{ std::abs( controlled->Mechanik->TrackBlock() ) }; - if( trackblockdistance <= 75.0 ) { - uitextline4 += " Another vehicle ahead (distance: " + to_string( trackblockdistance, 1 ) + " m)"; - } - } - } - } - - break; - } - - case( GLFW_KEY_F2 ) : { - // timetable - auto *vehicle { - ( FreeFlyModeFlag ? - std::get( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) : - controlled ) }; // w trybie latania lokalizujemy wg mapy - - if( vehicle == nullptr ) { break; } - // if the nearest located vehicle doesn't have a direct driver, try to query its owner - auto const owner = ( - ( ( vehicle->Mechanik != nullptr ) && ( vehicle->Mechanik->Primary() ) ) ? - vehicle->Mechanik : - vehicle->ctOwner ); - if( owner == nullptr ){ break; } - - auto const *table = owner->TrainTimetable(); - if( table == nullptr ) { break; } - - auto const &time = simulation::Time.data(); - uitextline1 = - "Time: " - + to_string( time.wHour ) + ":" - + ( time.wMinute < 10 ? "0" : "" ) + to_string( time.wMinute ) + ":" - + ( time.wSecond < 10 ? "0" : "" ) + to_string( time.wSecond ); - if( Global.iPause ) { - uitextline1 += " (paused)"; - } - - uitextline2 = Bezogonkow( owner->Relation(), true ) + " (" + Bezogonkow( owner->TrainName(), true ) + ")"; - auto const nextstation = Bezogonkow( owner->NextStop(), true ); - if( !nextstation.empty() ) { - // jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania - uitextline3 = " -> " + nextstation; - } - - if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) { - - if( 0 == table->StationCount ) { - // only bother if there's stations to list - UITable->text_lines.emplace_back( "(no timetable)", Global.UITextColor ); - } - else { - // header - UITable->text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor ); - - TMTableLine const *tableline; - for( int i = owner->iStationStart; i <= std::min( owner->iStationStart + 10, table->StationCount ); ++i ) { - // wyświetlenie pozycji z rozkładu - tableline = table->TimeTable + i; // linijka rozkładu - - std::string vmax = - " " - + to_string( tableline->vmax, 0 ); - vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej - std::string const station = ( - Bezogonkow( tableline->StationName, true ) - + " " ) - .substr( 0, 34 ); - std::string const location = ( - ( tableline->km > 0.0 ? - to_string( tableline->km, 2 ) : - "" ) - + " " ) - .substr( 0, 34 - tableline->StationWare.size() ); - std::string const arrival = ( - tableline->Ah >= 0 ? - to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Am ) ).substr( 1, 2 ) : - " | " ); - std::string const departure = ( - tableline->Dh >= 0 ? - to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Dm ) ).substr( 1, 2 ) : - " | " ); - auto const candeparture = ( - ( owner->iStationStart < table->StationIndex ) - && ( i < table->StationIndex ) - && ( ( time.wHour * 60 + time.wMinute ) >= ( tableline->Dh * 60 + tableline->Dm ) ) ); - auto traveltime = - " " - + ( i < 2 ? "" : - tableline->Ah >= 0 ? to_string( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), 0 ) : - to_string( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), 0 ) ); - traveltime = traveltime.substr( traveltime.size() - 3, 3 ); // z wyrównaniem do prawej - - UITable->text_lines.emplace_back( - ( "| " + vmax + " | " + station + " | " + arrival + " | " + traveltime + " |" ), - ( candeparture ? - glm::vec4( 0.0f, 1.0f, 0.0f, 1.0f ) :// czas minął i odjazd był, to nazwa stacji będzie na zielono - Global.UITextColor ) ); - UITable->text_lines.emplace_back( - ( "| | " + location + tableline->StationWare + " | " + departure + " | |" ), - ( candeparture ? - glm::vec4( 0.0f, 1.0f, 0.0f, 1.0f ) :// czas minął i odjazd był, to nazwa stacji będzie na zielono - Global.UITextColor ) ); - // divider/footer - UITable->text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor ); - } - if( owner->iStationStart + 10 < table->StationCount ) { - // if we can't display entire timetable, add a scrolling indicator at the bottom - UITable->text_lines.emplace_back( " ... ", Global.UITextColor ); - } - } - } - - break; - } - - case( GLFW_KEY_F3 ) : { - - auto *vehicle{ - ( FreeFlyModeFlag ? - std::get( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) : - controlled ) }; // w trybie latania lokalizujemy wg mapy - - if( vehicle != nullptr ) { - // jeśli domyślny ekran po pierwszym naciśnięciu - uitextline1 = "Vehicle name: " + vehicle->MoverParameters->Name; - - if( ( vehicle->Mechanik == nullptr ) && ( vehicle->ctOwner ) ) { - // for cars other than leading unit indicate the leader - uitextline1 += ", owned by " + vehicle->ctOwner->OwnerName(); - } - uitextline1 += "; Status: " + vehicle->MoverParameters->EngineDescription( 0 ); - // informacja o sprzęgach - uitextline1 += - "; C0:" + - ( vehicle->PrevConnected ? - vehicle->PrevConnected->name() + ":" + to_string( vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( - vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ? - " (" + to_string( vehicle->MoverParameters->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : - "" ) : - "none" ); - uitextline1 += - " C1:" + - ( vehicle->NextConnected ? - vehicle->NextConnected->name() + ":" + to_string( vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( - vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ? - " (" + to_string( vehicle->MoverParameters->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : - "" ) : - "none" ); - - // equipment flags - uitextline2 = ( vehicle->MoverParameters->Battery ? "B" : "." ); - uitextline2 += ( vehicle->MoverParameters->Mains ? "M" : "." ); - uitextline2 += ( vehicle->MoverParameters->PantRearUp ? ( vehicle->MoverParameters->PantRearVolt > 0.0 ? "O" : "o" ) : "." ); - uitextline2 += ( vehicle->MoverParameters->PantFrontUp ? ( vehicle->MoverParameters->PantFrontVolt > 0.0 ? "P" : "p" ) : "." ); - uitextline2 += ( vehicle->MoverParameters->PantPressLockActive ? "!" : ( vehicle->MoverParameters->PantPressSwitchActive ? "*" : "." ) ); - uitextline2 += ( vehicle->MoverParameters->WaterPump.is_active ? "W" : ( false == vehicle->MoverParameters->WaterPump.breaker ? "-" : ( vehicle->MoverParameters->WaterPump.is_enabled ? "w" : "." ) ) ); - uitextline2 += ( true == vehicle->MoverParameters->WaterHeater.is_damaged ? "!" : ( vehicle->MoverParameters->WaterHeater.is_active ? "H" : ( false == vehicle->MoverParameters->WaterHeater.breaker ? "-" : ( vehicle->MoverParameters->WaterHeater.is_enabled ? "h" : "." ) ) ) ); - uitextline2 += ( vehicle->MoverParameters->FuelPump.is_active ? "F" : ( vehicle->MoverParameters->FuelPump.is_enabled ? "f" : "." ) ); - uitextline2 += ( vehicle->MoverParameters->OilPump.is_active ? "O" : ( vehicle->MoverParameters->OilPump.is_enabled ? "o" : "." ) ); - uitextline2 += ( false == vehicle->MoverParameters->ConverterAllowLocal ? "-" : ( vehicle->MoverParameters->ConverterAllow ? ( vehicle->MoverParameters->ConverterFlag ? "X" : "x" ) : "." ) ); - uitextline2 += ( vehicle->MoverParameters->ConvOvldFlag ? "!" : "." ); - uitextline2 += ( vehicle->MoverParameters->CompressorFlag ? "C" : ( false == vehicle->MoverParameters->CompressorAllowLocal ? "-" : ( ( vehicle->MoverParameters->CompressorAllow || vehicle->MoverParameters->CompressorStart == start_t::automatic ) ? "c" : "." ) ) ); - uitextline2 += ( vehicle->MoverParameters->CompressorGovernorLock ? "!" : "." ); - - auto const train { Global.pWorld->train() }; - if( ( train != nullptr ) && ( train->Dynamic() == vehicle ) ) { - uitextline2 += ( vehicle->MoverParameters->Radio ? " R: " : " r: " ) + std::to_string( train->RadioChannel() ); - } -/* - uitextline2 += - " AnlgB: " + to_string( tmp->MoverParameters->AnPos, 1 ) - + "+" - + to_string( tmp->MoverParameters->LocalBrakePosA, 1 ) -*/ - uitextline2 += " Bdelay: "; - if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_G ) == bdelay_G ) - uitextline2 += "G"; - if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_P ) == bdelay_P ) - uitextline2 += "P"; - if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_R ) == bdelay_R ) - uitextline2 += "R"; - if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_M ) == bdelay_M ) - uitextline2 += "+Mg"; - - uitextline2 += ", Load: " + to_string( vehicle->MoverParameters->Load, 0 ) + " (" + to_string( vehicle->MoverParameters->LoadFlag, 0 ) + ")"; - - uitextline2 += - "; Pant: " - + to_string( vehicle->MoverParameters->PantPress, 2 ) - + ( vehicle->MoverParameters->bPantKurek3 ? "-ZG" : "|ZG" ); - - uitextline2 += - "; Ft: " + to_string( - vehicle->MoverParameters->Ft * 0.001f * ( - vehicle->MoverParameters->ActiveCab ? vehicle->MoverParameters->ActiveCab : - vehicle->ctOwner ? vehicle->ctOwner->Controlling()->ActiveCab : - 1 ), 1 ) - + ", Fb: " + to_string( vehicle->MoverParameters->Fb * 0.001f, 1 ) - + ", Fr: " + to_string( vehicle->MoverParameters->Adhesive( vehicle->MoverParameters->RunningTrack.friction ), 2 ) - + ( vehicle->MoverParameters->SlippingWheels ? " (!)" : "" ); - - if( vehicle->Mechanik ) { - uitextline2 += "; Ag: " + to_string( vehicle->Mechanik->fAccGravity, 2 ) + " (" + ( vehicle->Mechanik->fAccGravity > 0.01 ? "\\" : ( vehicle->Mechanik->fAccGravity < -0.01 ? "/" : "-" ) ) + ")"; - } - - uitextline2 += - "; TC:" - + to_string( vehicle->MoverParameters->TotalCurrent, 0 ); - auto const frontcouplerhighvoltage = - to_string( vehicle->MoverParameters->Couplers[ side::front ].power_high.voltage, 0 ) - + "@" - + to_string( vehicle->MoverParameters->Couplers[ side::front ].power_high.current, 0 ); - auto const rearcouplerhighvoltage = - to_string( vehicle->MoverParameters->Couplers[ side::rear ].power_high.voltage, 0 ) - + "@" - + to_string( vehicle->MoverParameters->Couplers[ side::rear ].power_high.current, 0 ); - uitextline2 += ", HV: "; - if( vehicle->MoverParameters->Couplers[ side::front ].power_high.local == false ) { - uitextline2 += - "(" + frontcouplerhighvoltage + ")-" - + ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:" - + "-(" + rearcouplerhighvoltage + ")"; - } - else { - uitextline2 += - frontcouplerhighvoltage - + ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:" - + rearcouplerhighvoltage; - } - - uitextline3 += - "TrB: " + to_string( vehicle->MoverParameters->BrakePress, 2 ) - + ", " + to_hex_str( vehicle->MoverParameters->Hamulec->GetBrakeStatus(), 2 ); - - uitextline3 += - "; LcB: " + to_string( vehicle->MoverParameters->LocBrakePress, 2 ) - + "; hat: " + to_string( vehicle->MoverParameters->BrakeCtrlPos2, 2 ) - + "; pipes: " + to_string( vehicle->MoverParameters->PipePress, 2 ) - + "/" + to_string( vehicle->MoverParameters->ScndPipePress, 2 ) - + "/" + to_string( vehicle->MoverParameters->EqvtPipePress, 2 ) - + ", MT: " + to_string( vehicle->MoverParameters->CompressedVolume, 3 ) - + ", BT: " + to_string( vehicle->MoverParameters->Volume, 3 ) - + ", CtlP: " + to_string( vehicle->MoverParameters->CntrlPipePress, 3 ) - + ", CtlT: " + to_string( vehicle->MoverParameters->Hamulec->GetCRP(), 3 ); - - if( vehicle->MoverParameters->ManualBrakePos > 0 ) { - - uitextline3 += "; manual brake on"; - } -/* - if( tmp->MoverParameters->LocalBrakePos > 0 ) { - - uitextline3 += ", local brake on"; - } - else { - - uitextline3 += ", local brake off"; - } -*/ - if( vehicle->Mechanik ) { - // o ile jest ktoś w środku - std::string flags = "cpapcplhhndoiefgvdpseil "; // flagi AI (definicja w Driver.h) - for( int i = 0, j = 1; i < 23; ++i, j <<= 1 ) - if( false == ( vehicle->Mechanik->DrivigFlags() & j ) ) // jak bit ustawiony - flags[ i ] = '.';// std::toupper( flags[ i ] ); // ^= 0x20; // to zmiana na wielką literę - - uitextline4 = flags; - - uitextline4 += - "Driver: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 ) - + " Ad=" + to_string( vehicle->Mechanik->AccDesired, 2 ) - + " Ah=" + to_string( vehicle->Mechanik->fAccThreshold, 2 ) - + "@" + to_string( vehicle->Mechanik->fBrake_a0[ 0 ], 2 ) - + "+" + to_string( vehicle->Mechanik->fBrake_a1[ 0 ], 2 ) - + " Bd=" + to_string( vehicle->Mechanik->fBrakeDist, 0 ) - + " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 ) - + " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 ) - + " VSl=" + to_string( vehicle->Mechanik->VelSignalLast, 0 ) - + " VLl=" + to_string( vehicle->Mechanik->VelLimitLast, 0 ) - + " VRd=" + to_string( vehicle->Mechanik->VelRoad, 0 ) - + " VRst=" + to_string( vehicle->Mechanik->VelRestricted, 0 ); - - if( ( vehicle->Mechanik->VelNext == 0.0 ) - && ( vehicle->Mechanik->eSignNext ) ) { - // jeśli ma zapamiętany event semafora, nazwa eventu semafora - uitextline4 += " (" + Bezogonkow( vehicle->Mechanik->eSignNext->asName ) + ")"; - } - - // biezaca komenda dla AI - uitextline4 += ", command: " + vehicle->Mechanik->OrderCurrent(); - } - - if( Global.iScreenMode[ Global.iTextMode - GLFW_KEY_F1 ] == 1 ) { - // f2 screen, track scan mode - if( vehicle->Mechanik == nullptr ) { - //żeby była tabelka, musi być AI - break; - } - - std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( vehicle->Mechanik->TableSize() ) - 1, 0, 30 ); - do { - std::string scanline = vehicle->Mechanik->TableText( i ); - if( scanline.empty() ) { break; } - UITable->text_lines.emplace_back( Bezogonkow( scanline ), Global.UITextColor ); - ++i; - } while( i < speedtablesize ); // TController:iSpeedTableSize TODO: change when the table gets recoded - } - } - else { - // wyświetlenie współrzędnych w scenerii oraz kąta kamery, gdy nie mamy wskaźnika - uitextline1 = - "Camera position: " - + to_string( camera->Pos.x, 2 ) + " " - + to_string( camera->Pos.y, 2 ) + " " - + to_string( camera->Pos.z, 2 ) - + ", azimuth: " - + to_string( 180.0 - glm::degrees( camera->Yaw ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód - + " " - + std::string( "S SEE NEN NWW SW" ) - .substr( 0 + 2 * floor( fmod( 8 + ( camera->Yaw + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); - // current luminance level - uitextline2 = "Light level: " + to_string( Global.fLuminance, 3 ); - if( Global.FakeLight ) { uitextline2 += "(*)"; } - } - - break; - } - - case( GLFW_KEY_F8 ) : { - // gfx renderer data - uitextline1 = - "FoV: " + to_string( Global.FieldOfView / Global.ZoomFactor, 1 ) - + ", Draw range x " + to_string( Global.fDistanceFactor, 1 ) -// + "; sectors: " + std::to_string( GfxRenderer.m_drawcount ) -// + ", FPS: " + to_string( Timer::GetFPS(), 2 ); - + ", FPS: " + std::to_string( static_cast(std::round(GfxRenderer.Framerate())) ); - if( Global.iSlowMotion ) { - uitextline1 += " (slowmotion " + to_string( Global.iSlowMotion ) + ")"; - } - - uitextline2 = - std::string( "Rendering mode: " ) - + ( Global.bUseVBO ? - "VBO" : - "Display Lists" ) - + " "; - if( false == Global.LastGLError.empty() ) { - uitextline2 += - "Last openGL error: " - + Global.LastGLError; - } - // renderer stats - uitextline3 = GfxRenderer.info_times(); - uitextline4 = GfxRenderer.info_stats(); - - break; - } - - case( GLFW_KEY_F9 ) : { - // informacja o wersji - uitextline1 = "MaSzyna " + Global.asVersion; // informacja o wersji - if( Global.iMultiplayer ) { - uitextline1 += " (multiplayer mode is active)"; - } - uitextline3 = - "vehicles: " + to_string( Timer::subsystem.sim_dynamics.average(), 2 ) + " msec" - + " update total: " + to_string( Timer::subsystem.sim_total.average(), 2 ) + " msec"; - // current event queue - auto const time { Timer::GetTime() }; - auto const *event { simulation::Events.begin() }; - auto eventtableindex{ 0 }; - while( ( event != nullptr ) - && ( eventtableindex < 30 ) ) { - - if( ( false == event->m_ignored ) - && ( true == event->bEnabled ) ) { - - auto const delay { " " + to_string( std::max( 0.0, event->fStartTime - time ), 1 ) }; - auto const eventline = - "Delay: " + delay.substr( delay.length() - 6 ) - + ", Event: " + event->asName - + ( event->Activator ? " (by: " + event->Activator->asName + ")" : "" ) - + ( event->evJoined ? " (joint event)" : "" ); - - UITable->text_lines.emplace_back( eventline, Global.UITextColor ); - ++eventtableindex; - } - event = event->evNext; - } - - break; - } - - case( GLFW_KEY_F10 ) : { - - uitextline1 = "Press [Y] key to quit / Aby zakonczyc program, przycisnij klawisz [Y]."; - - break; - } - - case( GLFW_KEY_F11 ): { - // scenario inspector - auto const *node { scene::Editor.node() }; - - if( node == nullptr ) { - auto const mouseposition { Global.pCamera->Pos + GfxRenderer.Mouse_Position() }; - uitextline1 = "mouse location: [" + to_string( mouseposition.x, 2 ) + ", " + to_string( mouseposition.y, 2 ) + ", " + to_string( mouseposition.z, 2 ) + "]"; - break; - } - - uitextline1 = - "node name: " + node->name() - + "; location: [" + to_string( node->location().x, 2 ) + ", " + to_string( node->location().y, 2 ) + ", " + to_string( node->location().z, 2 ) + "]" - + " (distance: " + to_string( glm::length( glm::dvec3{ node->location().x, 0.0, node->location().z } -glm::dvec3{ Global.pCameraPosition.x, 0.0, Global.pCameraPosition.z } ), 1 ) + " m)"; - // subclass-specific data - // TBD, TODO: specialized data dump method in each node subclass, or data imports in the panel for provided subclass pointer? - if( typeid( *node ) == typeid( TAnimModel ) ) { - - auto const *subnode = static_cast( node ); - - uitextline2 = "angle: " + to_string( clamp_circular( subnode->vAngle.y, 360.f ), 2 ) + " deg"; - uitextline2 += "; lights: "; - if( subnode->iNumLights > 0 ) { - uitextline2 += '['; - for( int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx ) { - uitextline2 += to_string( subnode->lsLights[ lightidx ] ); - if( lightidx < subnode->iNumLights - 1 ) { - uitextline2 += ", "; - } - } - uitextline2 += ']'; - } - else { - uitextline2 += "none"; - } - // 3d shape - auto modelfile { ( - subnode->pModel ? - subnode->pModel->NameGet() : - "none" ) }; - if( modelfile.find( szModelPath ) == 0 ) { - // don't include 'models/' in the path - modelfile.erase( 0, std::string{ szModelPath }.size() ); - } - // texture - auto texturefile { ( - subnode->Material()->replacable_skins[ 1 ] != null_handle ? - GfxRenderer.Material( subnode->Material()->replacable_skins[ 1 ] ).name : - "none" ) }; - if( texturefile.find( szTexturePath ) == 0 ) { - // don't include 'textures/' in the path - texturefile.erase( 0, std::string{ szTexturePath }.size() ); - } - uitextline3 = "mesh: " + modelfile; - uitextline4 = "skin: " + texturefile; - } - else if( typeid( *node ) == typeid( TTrack ) ) { - - auto const *subnode = static_cast( node ); - // basic attributes - uitextline2 = - "isolated: " + ( subnode->pIsolated ? subnode->pIsolated->asName : "none" ) - + "; velocity: " + to_string( subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity ) - + "; width: " + to_string( subnode->fTrackWidth ) + " m" - + "; friction: " + to_string( subnode->fFriction, 2 ) - + "; quality: " + to_string( subnode->iQualityFlag ); - // textures - auto texturefile { ( - subnode->m_material1 != null_handle ? - GfxRenderer.Material( subnode->m_material1 ).name : - "none" ) }; - if( texturefile.find( szTexturePath ) == 0 ) { - texturefile.erase( 0, std::string{ szTexturePath }.size() ); - } - auto texturefile2{ ( - subnode->m_material2 != null_handle ? - GfxRenderer.Material( subnode->m_material2 ).name : - "none" ) }; - if( texturefile2.find( szTexturePath ) == 0 ) { - texturefile2.erase( 0, std::string{ szTexturePath }.size() ); - } - uitextline2 += "; skins: [" + texturefile + ", " + texturefile2 + "]"; - // paths - uitextline3 = "paths: "; - for( auto const &path : subnode->m_paths ) { - uitextline3 += - "[" - + to_string( path.points[ segment_data::point::start ].x, 3 ) + ", " - + to_string( path.points[ segment_data::point::start ].y, 3 ) + ", " - + to_string( path.points[ segment_data::point::start ].z, 3 ) + "]->" - + "[" - + to_string( path.points[ segment_data::point::end ].x, 3 ) + ", " - + to_string( path.points[ segment_data::point::end ].y, 3 ) + ", " - + to_string( path.points[ segment_data::point::end ].z, 3 ) + "] "; - } - // events - std::vector< std::pair< std::string, TTrack::event_sequence const * > > const eventsequences { - { "ev0", &subnode->m_events0 }, { "ev0all", &subnode->m_events0all }, - { "ev1", &subnode->m_events1 }, { "ev1all", &subnode->m_events1all }, - { "ev2", &subnode->m_events2 }, { "ev2all", &subnode->m_events2all } }; - - for( auto const &eventsequence : eventsequences ) { - if( eventsequence.second->empty() ) { continue; } - uitextline4 += eventsequence.first + ": ["; - for( auto const &event : *( eventsequence.second ) ) { - if( uitextline4.back() != '[' ) { - uitextline4 += ", "; - } - if( event.second ) { - uitextline4 += event.second->asName; - } - } - uitextline4 += "] "; - } - - } - else if( typeid( *node ) == typeid( TMemCell ) ) { - - auto const *subnode = static_cast( node ); - - uitextline2 = - "data: [" + subnode->Text() + "]" - + " [" + to_string( subnode->Value1(), 2 ) + "]" - + " [" + to_string( subnode->Value2(), 2 ) + "]"; - uitextline3 = "track: " + ( subnode->asTrackName.empty() ? "none" : subnode->asTrackName ); - } - - break; - } - - case( GLFW_KEY_F12 ) : { - // opcje włączenia i wyłączenia logowania - uitextline1 = "[0] Debugmode " + std::string( DebugModeFlag ? "(on)" : "(off)" ); - uitextline2 = "[1] log.txt " + std::string( ( Global.iWriteLogEnabled & 1 ) ? "(on)" : "(off)" ); - uitextline3 = "[2] Console " + std::string( ( Global.iWriteLogEnabled & 2 ) ? "(on)" : "(off)" ); - - break; - } - - default: { - // uncovered cases, nothing to do here... - // ... unless we're in debug mode - if( DebugModeFlag ) { - - auto *vehicle { - ( FreeFlyModeFlag ? - std::get( simulation::Region->find_vehicle( camera->Pos, 20, false, false ) ) : - controlled ) }; // w trybie latania lokalizujemy wg mapy - if( vehicle == nullptr ) { - break; - } - uitextline1 = - "vel: " + to_string( vehicle->GetVelocity(), 2 ) + "/" + to_string( vehicle->MoverParameters->nrot* M_PI * vehicle->MoverParameters->WheelDiameter * 3.6, 2 ) - + " km/h;" + ( vehicle->MoverParameters->SlippingWheels ? " (!)" : " " ) - + " dist: " + to_string( vehicle->MoverParameters->DistCounter, 2 ) + " km" - + "; pos: [" + to_string( vehicle->GetPosition().x, 2 ) + ", " + to_string( vehicle->GetPosition().y, 2 ) + ", " + to_string( vehicle->GetPosition().z, 2 ) + "]" - + ", PM=" + to_string( vehicle->MoverParameters->WheelFlat, 1 ) - + " mm; enpwr=" + to_string( vehicle->MoverParameters->EnginePower, 1 ) - + "; enrot=" + to_string( vehicle->MoverParameters->enrot * 60, 0 ) - + " tmrot=" + to_string( std::abs( vehicle->MoverParameters->nrot ) * vehicle->MoverParameters->Transmision.Ratio * 60, 0 ) - + "; ventrot=" + to_string( vehicle->MoverParameters->RventRot * 60, 1 ) - + "; fanrot=" + to_string( vehicle->MoverParameters->dizel_heat.rpmw, 1 ) + ", " + to_string( vehicle->MoverParameters->dizel_heat.rpmw2, 1 ); - - uitextline2 = - "HamZ=" + to_string( vehicle->MoverParameters->fBrakeCtrlPos, 2 ) - + "; HamP=" + to_string( vehicle->MoverParameters->LocalBrakePosA, 2 ) - + "; NasJ=" + std::to_string( vehicle->MoverParameters->MainCtrlPos ) + "(" + std::to_string( vehicle->MoverParameters->MainCtrlActualPos ) + ")" - + ( ( vehicle->MoverParameters->ShuntMode && vehicle->MoverParameters->EngineType == TEngineType::DieselElectric ) ? - "; NasB=" + to_string( vehicle->MoverParameters->AnPos, 2 ) : - "; NasB=" + std::to_string( vehicle->MoverParameters->ScndCtrlPos ) + "(" + std::to_string( vehicle->MoverParameters->ScndCtrlActualPos ) + ")" ) - + "; I=" + - ( vehicle->MoverParameters->TrainType == dt_EZT ? - std::to_string( int( vehicle->MoverParameters->ShowCurrent( 0 ) ) ) : - std::to_string( int( vehicle->MoverParameters->Im ) ) ) - + "; U=" + to_string( int( vehicle->MoverParameters->RunningTraction.TractionVoltage + 0.5 ) ) - + "; R=" + - ( std::abs( vehicle->MoverParameters->RunningShape.R ) > 10000.0 ? - "~0.0" : - to_string( vehicle->MoverParameters->RunningShape.R, 1 ) ) - + " An=" + to_string( vehicle->MoverParameters->AccN, 2 ); // przyspieszenie poprzeczne - - if( tprev != simulation::Time.data().wSecond ) { - tprev = simulation::Time.data().wSecond; - Acc = ( vehicle->MoverParameters->Vel - VelPrev ) / 3.6; - VelPrev = vehicle->MoverParameters->Vel; - } - uitextline2 += "; As=" + to_string( Acc, 2 ); // przyspieszenie wzdłużne -/* - uitextline2 += " eAngle=" + to_string( std::cos( vehicle->MoverParameters->eAngle ), 2 ); -*/ - uitextline2 += "; oilP=" + to_string( vehicle->MoverParameters->OilPump.pressure_present, 3 ); - uitextline2 += " oilT=" + to_string( vehicle->MoverParameters->dizel_heat.To, 2 ); - uitextline2 += "; waterT=" + to_string( vehicle->MoverParameters->dizel_heat.temperatura1, 2 ); - uitextline2 += ( vehicle->MoverParameters->WaterCircuitsLink ? "-" : "|" ); - uitextline2 += to_string( vehicle->MoverParameters->dizel_heat.temperatura2, 2 ); - uitextline2 += "; engineT=" + to_string( vehicle->MoverParameters->dizel_heat.Ts, 2 ); - - uitextline3 = - "cyl.ham. " + to_string( vehicle->MoverParameters->BrakePress, 2 ) - + "; prz.gl. " + to_string( vehicle->MoverParameters->PipePress, 2 ) - + "; zb.gl. " + to_string( vehicle->MoverParameters->CompressedVolume, 2 ) - // youBy - drugi wezyk - + "; p.zas. " + to_string( vehicle->MoverParameters->ScndPipePress, 2 ); - - // McZapkie: warto wiedziec w jakim stanie sa przelaczniki - if( vehicle->MoverParameters->ConvOvldFlag ) - uitextline3 += " C! "; - else if( vehicle->MoverParameters->FuseFlag ) - uitextline3 += " F! "; - else if( !vehicle->MoverParameters->Mains ) - uitextline3 += " () "; - else { - switch( - vehicle->MoverParameters->ActiveDir * - ( vehicle->MoverParameters->Imin == vehicle->MoverParameters->IminLo ? - 1 : - 2 ) ) { - case 2: { uitextline3 += " >> "; break; } - case 1: { uitextline3 += " -> "; break; } - case 0: { uitextline3 += " -- "; break; } - case -1: { uitextline3 += " <- "; break; } - case -2: { uitextline3 += " << "; break; } - } - } - // McZapkie: predkosc szlakowa - if( vehicle->MoverParameters->RunningTrack.Velmax == -1 ) { - uitextline3 += " Vtrack=Vmax"; - } - else { - uitextline3 += " Vtrack " + to_string( vehicle->MoverParameters->RunningTrack.Velmax, 2 ); - } - - if( ( vehicle->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) - || ( vehicle->MoverParameters->TrainType == dt_EZT ) ) { - uitextline3 += - "; pant. " + to_string( vehicle->MoverParameters->PantPress, 2 ) - + ( vehicle->MoverParameters->bPantKurek3 ? "=" : "^" ) + "ZG"; - } - - // McZapkie: komenda i jej parametry - if( vehicle->MoverParameters->CommandIn.Command != ( "" ) ) { - uitextline4 = - "C:" + vehicle->MoverParameters->CommandIn.Command - + " V1=" + to_string( vehicle->MoverParameters->CommandIn.Value1, 0 ) - + " V2=" + to_string( vehicle->MoverParameters->CommandIn.Value2, 0 ); - } - if( ( vehicle->Mechanik ) - && ( vehicle->Mechanik->AIControllFlag == AIdriver ) ) { - uitextline4 += - "AI: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 ) - + " ad=" + to_string(vehicle->Mechanik->AccDesired, 2) - + "/" + to_string(vehicle->Mechanik->AccDesired*vehicle->Mechanik->BrakeAccFactor(), 2) - + " atrain=" + to_string(vehicle->Mechanik->fBrake_a0[0], 2) - + "+" + to_string(vehicle->Mechanik->fBrake_a1[0], 2) - + " aS=" + to_string(vehicle->Mechanik->AbsAccS_pub, 2) - + " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 ) - + " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 ); - } - - // induction motor data - if( vehicle->MoverParameters->EngineType == TEngineType::ElectricInductionMotor ) { - - UITable->text_lines.emplace_back( " eimc: eimv: press:", Global.UITextColor ); - for( int i = 0; i <= 20; ++i ) { - - std::string parameters = - vehicle->MoverParameters->eimc_labels[ i ] + to_string( vehicle->MoverParameters->eimc[ i ], 2, 9 ) - + " | " - + vehicle->MoverParameters->eimv_labels[ i ] + to_string( vehicle->MoverParameters->eimv[ i ], 2, 9 ); - - if( i < 10 ) { - parameters += " | " + train->fPress_labels[i] + to_string( train->fPress[ i ][ 0 ], 2, 9 ); - } - else if( i == 12 ) { - parameters += " med:"; - } - else if( i >= 13 ) { - parameters += " | " + vehicle->MED_labels[ i - 13 ] + to_string( vehicle->MED[ 0 ][ i - 13 ], 2, 9 ); - } - - UITable->text_lines.emplace_back( parameters, Global.UITextColor ); - } - } - if (vehicle->MoverParameters->EngineType == TEngineType::DieselEngine) { - std::string parameters = "param value"; - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "efill: " + to_string(vehicle->MoverParameters->dizel_fill, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "etorq: " + to_string(vehicle->MoverParameters->dizel_Torque, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "creal: " + to_string(vehicle->MoverParameters->dizel_engage, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "cdesi: " + to_string(vehicle->MoverParameters->dizel_engagestate, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "cdelt: " + to_string(vehicle->MoverParameters->dizel_engagedeltaomega, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "gears: " + to_string(vehicle->MoverParameters->dizel_automaticgearstatus, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hydro value"; - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCnI: " + to_string(vehicle->MoverParameters->hydro_TC_nIn, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCnO: " + to_string(vehicle->MoverParameters->hydro_TC_nOut, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCTM: " + to_string(vehicle->MoverParameters->hydro_TC_TMRatio, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCTI: " + to_string(vehicle->MoverParameters->hydro_TC_TorqueIn, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCTO: " + to_string(vehicle->MoverParameters->hydro_TC_TorqueOut, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCfl: " + to_string(vehicle->MoverParameters->hydro_TC_Fill, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - parameters = "hTCLR: " + to_string(vehicle->MoverParameters->hydro_TC_LockupRate, 2, 9); - UITable->text_lines.emplace_back(parameters, Global.UITextColor); - //parameters = "hTCXX: " + to_string(vehicle->MoverParameters->hydro_TC_nIn, 2, 9); - //UITable->text_lines.emplace_back(parameters, Global.UITextColor); - } - - } // if( DebugModeFlag && Controlled ) - - break; - } - } - -#ifdef EU07_USE_OLD_UI_CODE - if( Controlled && DebugModeFlag && !Global.iTextMode ) { - - uitextline1 += - ( "; d_omega " ) + to_string( Controlled->MoverParameters->dizel_engagedeltaomega, 3 ); - - if( Controlled->MoverParameters->EngineType == ElectricInductionMotor ) { - - for( int i = 0; i <= 8; i++ ) { - for( int j = 0; j <= 9; j++ ) { - glRasterPos2f( 0.05f + 0.03f * i, 0.16f - 0.01f * j ); - uitextline4 = to_string( Train->fEIMParams[ i ][ j ], 2 ); - } - } - } - } -#endif - - // update the ui header texts - auto &headerdata = UIHeader->text_lines; - headerdata[ 0 ].data = uitextline1; - headerdata[ 1 ].data = uitextline2; - headerdata[ 2 ].data = uitextline3; - headerdata[ 3 ].data = uitextline4; - - // stenogramy dźwięków (ukryć, gdy tabelka skanowania lub rozkład?) - auto &transcripts = UITranscripts->text_lines; - transcripts.clear(); - for( auto const &transcript : ui::Transcripts.aLines ) { - - if( Global.fTimeAngleDeg >= transcript.fShow ) { - - cParser parser( transcript.asText ); - while( true == parser.getTokens( 1, false, "|" ) ) { - - std::string transcriptline; parser >> transcriptline; - transcripts.emplace_back( transcriptline, glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f ) ); - } - } - } - -} - void ui_layer::render() { diff --git a/uilayer.h b/uilayer.h index ee6ea8e3..1cb72ec3 100644 --- a/uilayer.h +++ b/uilayer.h @@ -1,4 +1,12 @@ - +/* +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 #include @@ -30,29 +38,32 @@ struct ui_panel { class ui_layer { public: -// parameters: - -// constructors: +// constructors ui_layer() = default; -// destructor: - ~ui_layer(); +// destructor + virtual ~ui_layer(); -// methods: +// methods + static bool init( GLFWwindow *Window ); // assign texturing hardware unit + static void set_unit( GLint const Textureunit ) { m_textureunit = Textureunit; } // potentially processes provided input key. returns: true if the input was processed, false otherwise + virtual bool on_key( int const Key, int const Action ); // updates state of UI elements + virtual void - update(); + update() {} // draws requested UI elements void render(); // + static void set_cursor( int const Mode ); // stores operation progress @@ -70,12 +81,15 @@ public: void clear_texts() { m_panels.clear(); } void - push_back( std::shared_ptr Panel ) { m_panels.emplace_back( Panel ); } + push_back( ui_panel *Panel ) { m_panels.emplace_back( Panel ); } -// members: +protected: +// members + static GLFWwindow *m_window; + static bool m_cursorvisible; private: -// methods: +// methods // draws background quad with specified earlier texture void render_background(); @@ -94,11 +108,9 @@ private: // draws a quad between coordinates x,y and z,w with uv-coordinates spanning 0-1 void quad( glm::vec4 const &Coordinates, glm::vec4 const &Color ); - -// members: - GLFWwindow *m_window { nullptr }; - GLint m_textureunit{ GL_TEXTURE0 }; - GLuint m_fontbase { (GLuint)-1 }; // numer DL dla znaków w napisach +// members + static GLint m_textureunit; + static GLuint m_fontbase; // numer DL dla znaków w napisach // progress bar config. TODO: put these together into an object float m_progress { 0.0f }; // percentage of filled progres bar, to indicate lengthy operations. @@ -108,16 +120,6 @@ private: texture_handle m_background { null_handle }; // path to texture used as the background. size depends on mAspect. GLuint m_texture { 0 }; - std::vector > m_panels; + std::vector m_panels; std::string m_tooltip; - // TODO: clean these legacy components up - std::shared_ptr UIHeader; // header ui panel - std::shared_ptr UITable; // schedule or scan table - std::shared_ptr UITranscripts; // voice transcripts - int tprev; // poprzedni czas - double VelPrev; // poprzednia prędkość - double Acc; // przyspieszenie styczne - bool m_cursorvisible { false }; }; - -extern ui_layer UILayer; diff --git a/utilities.cpp b/utilities.cpp index 0a8e603b..7b8a17c3 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -281,7 +281,7 @@ std::string ToUpper(std::string const &text) { void win1250_to_ascii( std::string &Input ) { - std::unordered_map charmap{ + std::unordered_map const charmap { { 165, 'A' }, { 198, 'C' }, { 202, 'E' }, { 163, 'L' }, { 209, 'N' }, { 211, 'O' }, { 140, 'S' }, { 143, 'Z' }, { 175, 'Z' }, { 185, 'a' }, { 230, 'c' }, { 234, 'e' }, { 179, 'l' }, { 241, 'n' }, { 243, 'o' }, { 156, 's' }, { 159, 'z' }, { 191, 'z' } }; @@ -293,23 +293,33 @@ win1250_to_ascii( std::string &Input ) { } // Ra: tymczasowe rozwiązanie kwestii zagranicznych (czeskich) napisów -char bezogonkowo[] = "E?,?\"_++?%Sstzz" - " ^^L$A|S^CS<--RZo±,l'uP.,as>L\"lz" - "RAAAALCCCEEEEIIDDNNOOOOxRUUUUYTB" - "raaaalccceeeeiiddnnoooo-ruuuuyt?"; +char charsetconversiontable[] = + "E?,?\"_++?%Sstzz" + " ^^L$A|S^CS<--RZo±,l'uP.,as>L\"lz" + "RAAAALCCCEEEEIIDDNNOOOOxRUUUUYTB" + "raaaalccceeeeiiddnnoooo-ruuuuyt?"; -std::string Bezogonkow(std::string str, bool _) -{ // wycięcie liter z ogonkami, bo OpenGL nie umie wyświetlić - for (unsigned int i = 1; i < str.length(); ++i) - if (str[i] & 0x80) - str[i] = bezogonkowo[str[i] & 0x7F]; - else if (str[i] < ' ') // znaki sterujące nie są obsługiwane - str[i] = ' '; - else if (_) - if (str[i] == '_') // nazwy stacji nie mogą zawierać spacji - str[i] = ' '; // więc trzeba wyświetlać inaczej - return str; -}; +// wycięcie liter z ogonkami +std::string Bezogonkow(std::string Input, bool const Underscorestospaces) { + + char const extendedcharsetbit { static_cast( 0x80 ) }; + char const space { ' ' }; + char const underscore { '_' }; + + for( auto &input : Input ) { + if( input & extendedcharsetbit ) { + input = charsetconversiontable[ input ^ extendedcharsetbit ]; + } + else if( input < space ) { + input = space; + } + else if( Underscorestospaces && ( input == underscore ) ) { + input = space; + } + } + + return Input; +} template <> bool diff --git a/utilities.h b/utilities.h index 6c188c40..2b447f39 100644 --- a/utilities.h +++ b/utilities.h @@ -130,7 +130,7 @@ std::string ToUpper(std::string const &text); // replaces polish letters with basic ascii void win1250_to_ascii( std::string &Input ); // TODO: unify with win1250_to_ascii() -std::string Bezogonkow( std::string str, bool _ = false ); +std::string Bezogonkow( std::string Input, bool const Underscorestospaces = false ); inline std::string diff --git a/version.h b/version.h index 3272ba5f..3d4afa1c 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 718 +#define VERSION_MINOR 802 #define VERSION_REVISION 0 diff --git a/windows.cpp b/windows.cpp index a25e6ae7..70c29127 100644 --- a/windows.cpp +++ b/windows.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "World.h" +#include "messaging.h" #include "utilities.h" #pragma warning (disable: 4091) @@ -62,7 +62,7 @@ LRESULT APIENTRY WndProc( HWND hWnd, // handle for this window // obsługa danych przesłanych przez program sterujący pDane = (PCOPYDATASTRUCT)lParam; if( pDane->dwData == MAKE_ID4('E', 'U', '0', '7')) // sygnatura danych - World.OnCommandGet( ( multiplayer::DaneRozkaz *)( pDane->lpData ) ); + multiplayer::OnCommandGet( ( multiplayer::DaneRozkaz *)( pDane->lpData ) ); break; } }