diff --git a/Driver.cpp b/Driver.cpp index a85b2729..8746c591 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2985,20 +2985,27 @@ void TController::RecognizeCommand() c->Command = ""; // usunięcie obsłużonej komendy } -void TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, - const TLocation &NewLocation, TStopReason reason) +void TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason) { // wysłanie komendy przez event PutValues, jak pojazd ma obsadę, to wysyła tutaj, a nie do pojazdu // bezpośrednio +#ifdef EU07_USE_OLD_GROUNDCODE vector3 sl; sl.x = -NewLocation.X; // zamiana na współrzędne scenerii sl.z = NewLocation.Y; sl.y = NewLocation.Z; +#else + // zamiana na współrzędne scenerii + glm::dvec3 sl { -NewLocation.X, NewLocation.Y, NewLocation.Z }; +#endif if (!PutCommand(NewCommand, NewValue1, NewValue2, &sl, reason)) mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation); } -bool TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, - const vector3 *NewLocation, TStopReason reason) +#ifdef EU07_USE_OLD_GROUNDCODE +bool TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason) +#else +bool TController::PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason ) +#endif { // analiza komendy if (NewCommand == "CabSignal") { // SHP wyzwalane jest przez człon z obsadą, ale obsługiwane przez silnikowy @@ -4229,13 +4236,12 @@ TController::UpdateSituation(double dt) { ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders if (fabs(VelSignal) >= 1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą - PutCommand("SetVelocity", VelSignal, VelNext, - NULL); // komenda robi dodatkowe operacje + PutCommand("SetVelocity", VelSignal, VelNext, nullptr); // komenda robi dodatkowe operacje break; case cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy if (!(OrderList[OrderPos] & ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders - PutCommand("ShuntVelocity", VelSignal, VelNext, NULL); + PutCommand("ShuntVelocity", VelSignal, VelNext, nullptr); else if (iCoupler) // jeśli jedzie w celu połączenia SetVelocity(VelSignal, VelNext); break; @@ -5306,7 +5312,11 @@ TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, } // sprawdzanie zdarzeń semaforów i ograniczeń szlakowych +#ifdef EU07_USE_OLD_GROUNDCODE void TController::SetProximityVelocity(double dist, double vel, const vector3 *pos) +#else +void TController::SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ) +#endif { // Ra:przeslanie do AI prędkości /* //!!!! zastąpić prawidłową reakcją AI na SetProximityVelocity !!!! @@ -5318,7 +5328,7 @@ void TController::SetProximityVelocity(double dist, double vel, const vector3 *p if ((vel<0)?true:dist>0.1*(MoverParameters->Vel*MoverParameters->Vel-vel*vel)+50) {//jeśli jest dalej od umownej drogi hamowania */ - PutCommand("SetProximityVelocity", dist, vel, pos); + PutCommand( "SetProximityVelocity", dist, vel, pos ); /* } else diff --git a/Driver.h b/Driver.h index 09d7326a..d669b498 100644 --- a/Driver.h +++ b/Driver.h @@ -313,10 +313,12 @@ private: public: Mtable::TTrainParameters *Timetable() { return TrainParams; }; - void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, - const TLocation &NewLocation, TStopReason reason = stopComm); - bool PutCommand(std::string NewCommand, double NewValue1, double NewValue2, - const vector3 *NewLocation, TStopReason reason = stopComm); + void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason = stopComm); +#ifdef EU07_USE_OLD_GROUNDCODE + bool PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason = stopComm); +#else + bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm ); +#endif void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę // procedury dotyczace rozkazow dla maszynisty // uaktualnia informacje o prędkości @@ -376,7 +378,11 @@ private: bool BackwardTrackBusy(TTrack *Track); TEvent *CheckTrackEventBackward(double fDirection, TTrack *Track); TTrack *BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, TEvent *&Event); +#ifdef EU07_USE_OLD_GROUNDCODE void SetProximityVelocity(double dist, double vel, const vector3 *pos); +#else + void SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ); +#endif TCommandType BackwardScan(); public: diff --git a/DynObj.cpp b/DynObj.cpp index cbf34062..beb068b2 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2613,77 +2613,36 @@ bool TDynamicObject::Update(double dt, double dt1) NoVoltTime = 0; tmpTraction.TractionVoltage = v; } - else - { - /* - if (MoverParameters->Vel>0.1f) //jeśli jedzie - if (NoVoltTime==0.0) //tylko przy pierwszym zaniku napięcia - if (MoverParameters->PantFrontUp||MoverParameters->PantRearUp) - //if - ((pants[0].fParamPants->PantTraction>1.0)||(pants[1].fParamPants->PantTraction>1.0)) - {//wspomagacz usuwania problemów z siecią - if (!Global::iPause) - {//Ra: tymczasowa teleportacja do miejsca, gdzie brakuje prądu - Global::SetCameraPosition(vPosition+vector3(0,0,5)); //nowa - pozycja dla - generowania obiektów - Global::pCamera->Init(vPosition+vector3(0,0,5),Global::pFreeCameraInitAngle[0]); - //przestawienie - } - Global:l::pGround->Silence(Global::pCamera->Pos); //wyciszenie - wszystkiego - z poprzedniej pozycji - Globa:iPause|=1; //tymczasowe zapauzowanie, gdy problem z - siecią - } - */ - NoVoltTime = NoVoltTime + dt; - if (NoVoltTime > 0.2) // jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod - // izolatorem daje 0.15s) - { // Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało - // WS pod - // izolatorem - if (MoverParameters->Vel > 0.5) // jeśli jedzie - if (MoverParameters->PantFrontUp || - MoverParameters->PantRearUp) // Ra 2014-07: doraźna blokada logowania - // zimnych lokomotyw - zrobić to trzeba - // inaczej + else { + NoVoltTime += dt; + if( NoVoltTime > 0.2 ) { + // jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod izolatorem daje 0.15s) + // Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało WS pod izolatorem + if( MoverParameters->Vel > 0.5 ) { + // jeśli jedzie + // Ra 2014-07: doraźna blokada logowania zimnych lokomotyw - zrobić to trzeba inaczej + if( MoverParameters->PantFrontUp || MoverParameters->PantRearUp ) // if (NoVoltTime>0.02) //tu można ograniczyć czas rozłączenia // if (DebugModeFlag) //logowanie nie zawsze - if ((MoverParameters->Mains) && - ((MoverParameters->EngineType != ElectricInductionMotor) - || (MoverParameters->GetTrainsetVoltage() < 0.1f))) - { // Ra 15-01: logować tylko, jeśli WS załączony - // yB 16-03: i nie jest to asynchron zasilany z daleka - // if (MoverParameters->PantFrontUp&&pants) + if( ( MoverParameters->Mains ) + && ( ( MoverParameters->EngineType != ElectricInductionMotor ) + || ( MoverParameters->GetTrainsetVoltage() < 0.1f ) ) ) { + // Ra 15-01: logować tylko, jeśli WS załączony + // yB 16-03: i nie jest to asynchron zasilany z daleka // Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne, // więc lepiej się tego nie zaloguje - ErrorLog("Voltage loss: by " + MoverParameters->Name + " at " + - to_string(vPosition.x, 2, 7) + " " + - to_string(vPosition.y, 2, 7) + " " + - to_string(vPosition.z, 2, 7) + ", time " + - to_string(NoVoltTime, 2, 7)); - // if (MoverParameters->PantRearUp) - // if (iAnimType[ANIM_PANTS]>1) - // if (pants[1]) - // ErrorLog("Voltage loss: by "+MoverParameters->Name+" at - // "+FloatToStrF(vPosition.x,ffFixed,7,2)+" - // "+FloatToStrF(vPosition.y,ffFixed,7,2)+" - // "+FloatToStrF(vPosition.z,ffFixed,7,2)+", time - // "+FloatToStrF(NoVoltTime,ffFixed,7,2)); + ErrorLog( + "Bad traction: " + MoverParameters->Name + + " lost power for " + to_string( NoVoltTime, 2 ) + " sec. at " + + to_string( glm::dvec3{ vPosition } ) ); } - // Ra 2F1H: nie było sensu wpisywać tu zera po upływie czasu, bo - // zmienna była + } + // Ra 2F1H: nie było sensu wpisywać tu zera po upływie czasu, bo zmienna była // tymczasowa, a napięcie zerowane od razu tmpTraction.TractionVoltage = 0; // Ra 2013-12: po co tak? - // pControlled->MainSwitch(false); //może tak? } } } - // else //Ra: nie no, trzeba podnieść pantografy, jak nie będzie drutu, to - // będą miały prąd - // po osiągnięciu 1.4m - // tmpTraction.TractionVoltage=0.95*MoverParameters->EnginePowerSource.MaxVoltage; } else tmpTraction.TractionVoltage = 0.95 * MoverParameters->EnginePowerSource.MaxVoltage; @@ -3199,22 +3158,22 @@ bool TDynamicObject::Update(double dt, double dt1) else if ( ( true == MoverParameters->PantRearUp ) && ( PantDiff < 0.01 ) ) { - if ((MoverParameters->PantRearVolt == 0.0) && - (MoverParameters->PantFrontVolt == 0.0)) - sPantUp.Play(vol, 0, MechInside, vPosition); - if (p->hvPowerWire) // TODO: wyliczyć trzeba prąd przypadający na - // pantograf i - // wstawić do GetVoltage() - { - MoverParameters->PantRearVolt = - p->hvPowerWire->VoltageGet(MoverParameters->Voltage, fPantCurrent); + if( ( MoverParameters->PantRearVolt == 0.0 ) + && ( MoverParameters->PantFrontVolt == 0.0 ) ) { + sPantUp.Play( vol, 0, MechInside, vPosition ); + } + if (p->hvPowerWire) { + // TODO: wyliczyć trzeba prąd przypadający na pantograf i wstawić do GetVoltage() + MoverParameters->PantRearVolt = p->hvPowerWire->VoltageGet( MoverParameters->Voltage, fPantCurrent ); fCurrent -= fPantCurrent; // taki prąd płynie przez powyższy pantograf } else MoverParameters->PantRearVolt = 0.0; } - else + else { +// Global::iPause ^= 2; MoverParameters->PantRearVolt = 0.0; + } break; } // pozostałe na razie nie obsługiwane if( MoverParameters->PantPress > ( @@ -3281,8 +3240,8 @@ bool TDynamicObject::Update(double dt, double dt1) p->fAngleU = acos((p->fLenL1 * cos(k) + p->fHoriz) / p->fLenU1); // górne ramię // wyliczyć aktualną wysokość z wzoru sinusowego // h=a*sin()+b*sin() - p->PantWys = p->fLenL1 * sin(k) + p->fLenU1 * sin(p->fAngleU) + - p->fHeight; // wysokość całości + // wysokość całości + p->PantWys = p->fLenL1 * sin(k) + p->fLenU1 * sin(p->fAngleU) + p->fHeight; } } } // koniec pętli po pantografach @@ -4438,10 +4397,8 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, // pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów // asymetrycznych pants[ i ].fParamPants->PantTraction = pants[ i ].fParamPants->PantWys; - pants[ i ].fParamPants->fWidth = - 0.5 * - MoverParameters->EnginePowerSource.CollectorParameters - .CSW; // połowa szerokości ślizgu; jest w "Power: CSW=" + // połowa szerokości ślizgu; jest w "Power: CSW=" + pants[ i ].fParamPants->fWidth = 0.5 * MoverParameters->EnginePowerSource.CollectorParameters.CSW; } } } @@ -4932,7 +4889,12 @@ void TDynamicObject::RadioStop() if( ( MoverParameters->SecuritySystem.RadioStop ) && ( MoverParameters->Radio ) ) { // jeśli pojazd ma RadioStop i jest on aktywny +#ifdef EU07_USE_OLD_GROUNDCODE Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &vPosition, stopRadio ); +#else + // HAX cast until math types unification + Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &static_cast(vPosition), stopRadio ); +#endif // add onscreen notification for human driver // TODO: do it selectively for the 'local' driver once the multiplayer is in if( false == Mechanik->AIControllFlag ) { @@ -5404,10 +5366,8 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { for( auto *vehicle : m_items ) { // Ra: zmienić warunek na sprawdzanie pantografów w jednej zmiennej: czy pantografy i czy podniesione if( vehicle->MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) { -/* // TODO: re-implement - GetTraction( vehicle ); -*/ + update_traction( vehicle ); } vehicle->MoverParameters->ComputeConstans(); vehicle->CoupleDist(); @@ -5447,3 +5407,97 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { } */ } + +// legacy method, checks for presence and height of traction wire for specified vehicle +void +vehicle_table::update_traction( TDynamicObject *Vehicle ) { + + auto const vFront = glm::make_vec3( Vehicle->VectorFront().getArray() ); // wektor normalny dla płaszczyzny ruchu pantografu + auto const vUp = glm::make_vec3( Vehicle->VectorUp().getArray() ); // wektor pionu pudła (pochylony od pionu na przechyłce) + auto const vLeft = glm::make_vec3( Vehicle->VectorLeft().getArray() ); // wektor odległości w bok (odchylony od poziomu na przechyłce) + auto const position = glm::dvec3 { Vehicle->GetPosition() }; // współrzędne środka pojazdu + + for( int pantographindex = 0; pantographindex < Vehicle->iAnimType[ ANIM_PANTS ]; ++pantographindex ) { + // pętla po pantografach + auto pantograph { Vehicle->pants[ pantographindex ].fParamPants }; + if( true == ( + pantographindex == TMoverParameters::side::front ? + Vehicle->MoverParameters->PantFrontUp : + Vehicle->MoverParameters->PantRearUp ) ) { + // jeśli pantograf podniesiony + auto const pant0 { position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ) }; + if( pantograph->hvPowerWire != nullptr ) { + // jeżeli znamy drut z poprzedniego przebiegu + for( int attempts = 0; attempts < 30; ++attempts ) { + // powtarzane aż do znalezienia odpowiedniego odcinka na liście dwukierunkowej + if( pantograph->hvPowerWire->iLast & 0x3 ) { + // dla ostatniego i przedostatniego przęsła wymuszamy szukanie innego + // nie to, że nie ma, ale trzeba sprawdzić inne + pantograph->hvPowerWire = nullptr; + break; + } + if( pantograph->hvPowerWire->hvParallel ) { + // jeśli przęsło tworzy bieżnię wspólną, to trzeba sprawdzić pozostałe + // nie to, że nie ma, ale trzeba sprawdzić inne + pantograph->hvPowerWire = nullptr; + break; + } + // obliczamy wyraz wolny równania płaszczyzny (to miejsce nie jest odpowienie) + // podstawiamy równanie parametryczne drutu do równania płaszczyzny pantografu + auto const fRaParam = + -( glm::dot( pantograph->hvPowerWire->pPoint1, vFront ) - glm::dot( pant0, vFront ) ) + / glm::dot( pantograph->hvPowerWire->vParametric, vFront ); + + if( fRaParam < -0.001 ) { + // histereza rzędu 7cm na 70m typowego przęsła daje 1 promil + pantograph->hvPowerWire = pantograph->hvPowerWire->hvNext[ 0 ]; + continue; + } + if( fRaParam > 1.001 ) { + pantograph->hvPowerWire = pantograph->hvPowerWire->hvNext[ 1 ]; + continue; + } + // jeśli t jest w przedziale, wyznaczyć odległość wzdłuż wektorów vUp i vLeft + // punkt styku płaszczyzny z drutem (dla generatora łuku el.) + auto const vStyk { pantograph->hvPowerWire->pPoint1 + fRaParam * pantograph->hvPowerWire->vParametric }; + auto const vGdzie { vStyk - pant0 }; // wektor + // odległość w pionie musi być w zasięgu ruchu "pionowego" pantografu + // musi się mieścić w przedziale ruchu pantografu + auto const fVertical { glm::dot( vGdzie, vUp ) }; + // odległość w bok powinna być mniejsza niż pół szerokości pantografu + // to się musi mieścić w przedziale zależnym od szerokości pantografu + auto const fHorizontal { std::abs( glm::dot( vGdzie, vLeft ) ) - pantograph->fWidth }; + // jeśli w pionie albo w bok jest za daleko, to dany drut jest nieużyteczny + if( fHorizontal <= 0.0 ) { + // koniec pętli, aktualny drut pasuje + pantograph->PantTraction = fVertical; + break; + } + else { + // the wire is outside contact area and as of now we don't have good detection of parallel sections + // as such there's no guaratee there isn't parallel section present. + // therefore we don't bother checking if the wire is still within range of guide horns + // but simply force area search for potential better option + pantograph->hvPowerWire = nullptr; + break; + } + } + } + + if( pantograph->hvPowerWire == nullptr ) { + // look in the region for a suitable traction piece if we don't already have any + simulation::Region->update_traction( Vehicle, pantographindex ); + } + + if( ( pantograph->hvPowerWire == nullptr ) + && ( false == Global::bLiveTraction ) ) { + // jeśli drut nie znaleziony ale można oszukiwać to dajemy coś tam dla picu + Vehicle->pants[ pantographindex ].fParamPants->PantTraction = 1.4; + } + } + else { + // pantograph is down + pantograph->hvPowerWire = nullptr; + } + } +} diff --git a/DynObj.h b/DynObj.h index df0794e9..8670ea20 100644 --- a/DynObj.h +++ b/DynObj.h @@ -492,6 +492,9 @@ public: // legacy method, calculates changes in simulation state over specified time void update( double dt, int iter ); + // legacy method, checks for presence and height of traction wire for specified vehicle + void + update_traction( TDynamicObject *Vehicle ); }; //--------------------------------------------------------------------------- diff --git a/EvLaunch.cpp b/EvLaunch.cpp index 4fda3368..349a29db 100644 --- a/EvLaunch.cpp +++ b/EvLaunch.cpp @@ -17,36 +17,14 @@ http://mozilla.org/MPL/2.0/. #include "EvLaunch.h" #include "Globals.h" #include "Logs.h" -#include "Usefull.h" -#include "McZapkie/mctools.h" #include "Event.h" #include "MemCell.h" -#include "mtable.h" #include "Timer.h" #include "parser.h" #include "Console.h" -using namespace Mtable; - //--------------------------------------------------------------------------- -TEventLauncher::TEventLauncher() -{ // ustawienie początkowych wartości dla wszystkich zmiennych - iKey = 0; - DeltaTime = -1; - UpdatedTime = 0; - fVal1 = fVal2 = 0; - iHour = iMinute = -1; // takiego czasu nigdy nie będzie - dRadius = 0; - Event1 = Event2 = NULL; - MemCell = NULL; - iCheckMask = 0; -} - -void TEventLauncher::Init() -{ -} - // encodes expected key in a short, where low byte represents the actual key, // and the high byte holds modifiers: 0x1 = shift, 0x2 = ctrl, 0x4 = alt int vk_to_glfw_key( int const Keycode ) { @@ -145,7 +123,7 @@ bool TEventLauncher::Load(cParser *parser) return true; }; -bool TEventLauncher::Render() +bool TEventLauncher::check_conditions() { //"renderowanie" wyzwalacza bool bCond = false; if (iKey != 0) @@ -199,13 +177,13 @@ bool TEventLauncher::Render() return bCond; // sprawdzanie dRadius w Ground.cpp } -bool TEventLauncher::IsGlobal() -{ // sprawdzenie, czy jest globalnym wyzwalaczem czasu - if (DeltaTime == 0) - if (iHour >= 0) - if (iMinute >= 0) - if (dRadius < 0.0) // bez ograniczenia zasięgu - return true; - return false; +// sprawdzenie, czy jest globalnym wyzwalaczem czasu +bool TEventLauncher::IsGlobal() const { + + return ( ( DeltaTime == 0 ) + && ( iHour >= 0 ) + && ( iMinute >= 0 ) + && ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu }; + //--------------------------------------------------------------------------- diff --git a/EvLaunch.h b/EvLaunch.h index 42ce2c73..1dbeae97 100644 --- a/EvLaunch.h +++ b/EvLaunch.h @@ -10,32 +10,43 @@ http://mozilla.org/MPL/2.0/. #pragma once #include -#include "Classes.h" -class TEventLauncher -{ - private: - int iKey; - double DeltaTime; - double UpdatedTime; - double fVal1; - double fVal2; - std::string szText; - int iHour, iMinute; // minuta uruchomienia - public: - double dRadius; +#include "Classes.h" +#include "scenenode.h" + +class TEventLauncher : public editor::basic_node { + +public: +// constructor + TEventLauncher( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} + // legacy constructor + TEventLauncher() = default; + +// methods + bool Load( cParser *parser ); + // checks conditions associated with the event. returns: true if the conditions are met + bool check_conditions(); + bool IsGlobal() const; +// members std::string asEvent1Name; std::string asEvent2Name; std::string asMemCellName; - TEvent *Event1; - TEvent *Event2; - TMemCell *MemCell; - int iCheckMask; - TEventLauncher(); - void Init(); - bool Load(cParser *parser); - bool Render(); - bool IsGlobal(); + TEvent *Event1 { nullptr }; + TEvent *Event2 { nullptr }; + TMemCell *MemCell { nullptr }; + int iCheckMask { 0 }; + double dRadius { 0.0 }; + +private: +// members + int iKey { 0 }; + double DeltaTime { -1.0 }; + double UpdatedTime { 0.0 }; + double fVal1 { 0.0 }; + double fVal2 { 0.0 }; + std::string szText; + int iHour { -1 }; + int iMinute { -1 }; // minuta uruchomienia }; //--------------------------------------------------------------------------- diff --git a/Event.cpp b/Event.cpp index 389b92ca..398b7699 100644 --- a/Event.cpp +++ b/Event.cpp @@ -727,6 +727,32 @@ event_manager::~event_manager() { } } +// adds specified event launcher to the list of global launchers +void +event_manager::queue( TEventLauncher *Launcher ) { + + m_launcherqueue.emplace_back( Launcher ); +} + +// legacy method, updates event queues +void +event_manager::update() { + + // process currently queued events + CheckQuery(); + // test list of global events for possible new additions to the queue + for( auto *launcher : m_launcherqueue ) { + + if( true == launcher->check_conditions() ) { + // NOTE: we're presuming global events aren't going to use event2 + WriteLog( "Eventlauncher " + launcher->name() ); + if( launcher->Event1 ) { + Global::AddToQuery( launcher->Event1, nullptr ); + } + } + } +} + // adds provided event to the collection. returns: true on success // TODO: return handle instead of pointer bool @@ -826,7 +852,11 @@ event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) { for( auto dynamic : Event->Params[ 6 ].asTrack->Dynamics ) { Event->Params[ 5 ].asMemCell->PutCommand( dynamic->Mechanik, +#ifdef EU07_USE_OLD_GROUNDCODE &Event->Params[ 4 ].nGroundNode->pCenter ); +#else + Event->Params[ 4 ].asLocation ); +#endif } //if (DebugModeFlag) WriteLog( @@ -924,7 +954,11 @@ event_manager::CheckQuery() { for( auto dynamic : m_workevent->Params[ 6 ].asTrack->Dynamics ) { m_workevent->Params[ 5 ].asMemCell->PutCommand( dynamic->Mechanik, +#ifdef EU07_USE_OLD_GROUNDCODE &m_workevent->Params[ 4 ].nGroundNode->pCenter ); +#else + m_workevent->Params[ 4 ].asLocation ); +#endif } //if (DebugModeFlag) WriteLog("Type: UpdateValues & Track command - [" + @@ -950,7 +984,11 @@ event_manager::CheckQuery() { */ m_workevent->Params[ 9 ].asMemCell->PutCommand( m_workevent->Activator->Mechanik, +#ifdef EU07_USE_OLD_GROUNDCODE &m_workevent->Params[ 8 ].nGroundNode->pCenter ); +#else + m_workevent->Params[ 8 ].asLocation ); +#endif } WriteLog( "Type: GetValues" ); break; @@ -1273,263 +1311,263 @@ void event_manager::InitEvents() { //łączenie eventów z pozostałymi obiektami - for( auto *Current : m_events ) { + for( auto *event : m_events ) { - switch (Current->Type) { + switch( event->Type ) { case tp_AddValues: // sumowanie wartości case tp_UpdateValues: { // zmiana wartości - auto *cell = simulation::Memory.find( Current->asNodeName ); // nazwa komórki powiązanej z eventem + auto *cell = simulation::Memory.find( event->asNodeName ); // nazwa komórki powiązanej z eventem if( cell != nullptr ) { // McZapkie-100302 - if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { + if( event->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { // jeśli chodzi o zajetosc toru (tor może być inny, niż wpisany w komórce) // nazwa toru ta sama, co nazwa komórki - Current->Params[ 9 ].asTrack = simulation::Paths.find( Current->asNodeName ); - if( Current->Params[ 9 ].asTrack == nullptr ) { - ErrorLog( "Bad event: track \"" + Current->asNodeName + "\" referenced in event \"" + Current->asName + "\" doesn't exist" ); + event->Params[ 9 ].asTrack = simulation::Paths.find( event->asNodeName ); + if( event->Params[ 9 ].asTrack == nullptr ) { + ErrorLog( "Bad event: track \"" + event->asNodeName + "\" referenced in event \"" + event->asName + "\" doesn't exist" ); } } - Current->Params[ 4 ].asLocation = &( cell->location() ); - Current->Params[ 5 ].asMemCell = cell; // komórka do aktualizacji - if( Current->iFlags & ( conditional_memcompare ) ) { + event->Params[ 4 ].asLocation = &( cell->location() ); + event->Params[ 5 ].asMemCell = cell; // komórka do aktualizacji + if( event->iFlags & ( conditional_memcompare ) ) { // komórka do badania warunku - Current->Params[ 9 ].asMemCell = cell; + event->Params[ 9 ].asMemCell = cell; } if( false == cell->asTrackName.empty() ) { // tor powiązany z komórką powiązaną z eventem // tu potrzebujemy wskaźnik do komórki w (tmp) - Current->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); - if( Current->Params[ 6 ].asTrack == nullptr ) { + event->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); + if( event->Params[ 6 ].asTrack == nullptr ) { ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exist" ); } } else { - Current->Params[ 6 ].asTrack = nullptr; + event->Params[ 6 ].asTrack = nullptr; } } else { // nie ma komórki, to nie będzie działał poprawnie - Current->m_ignored = true; // deaktywacja - ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + event->m_ignored = true; // deaktywacja + ErrorLog( "Bad event: event \"" + event->asName + "\" cannot find memcell \"" + event->asNodeName + "\"" ); } break; } case tp_LogValues: { // skojarzenie z memcell - if( Current->asNodeName.empty() ) { // brak skojarzenia daje logowanie wszystkich - Current->Params[ 9 ].asMemCell = nullptr; + if( event->asNodeName.empty() ) { // brak skojarzenia daje logowanie wszystkich + event->Params[ 9 ].asMemCell = nullptr; break; } } case tp_GetValues: case tp_WhoIs: { - auto *cell = simulation::Memory.find( Current->asNodeName ); + auto *cell = simulation::Memory.find( event->asNodeName ); if( cell != nullptr ) { - Current->Params[ 8 ].asLocation = &( cell->location() ); - Current->Params[ 9 ].asMemCell = cell; - if( ( Current->Type == tp_GetValues ) + event->Params[ 8 ].asLocation = &( cell->location() ); + event->Params[ 9 ].asMemCell = cell; + if( ( event->Type == tp_GetValues ) && ( cell->IsVelocity() ) ) { // jeśli odczyt komórki a komórka zawiera komendę SetVelocity albo ShuntVelocity // to event nie będzie dodawany do kolejki - Current->bEnabled = false; + event->bEnabled = false; } } else { // nie ma komórki, to nie będzie działał poprawnie - Current->m_ignored = true; // deaktywacja - ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + event->m_ignored = true; // deaktywacja + ErrorLog( "Bad event: event \"" + event->asName + "\" cannot find memcell \"" + event->asNodeName + "\"" ); } break; } case tp_CopyValues: { // skopiowanie komórki do innej - auto *cell = simulation::Memory.find( Current->asNodeName ); // komórka docelowa + auto *cell = simulation::Memory.find( event->asNodeName ); // komórka docelowa if( cell != nullptr ) { - Current->Params[ 4 ].asLocation = &( cell->location() ); - Current->Params[ 5 ].asMemCell = cell; // komórka docelowa + event->Params[ 4 ].asLocation = &( cell->location() ); + event->Params[ 5 ].asMemCell = cell; // komórka docelowa if( false == cell->asTrackName.empty() ) { // tor powiązany z komórką powiązaną z eventem // tu potrzebujemy wskaźnik do komórki w (tmp) - Current->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); - if( Current->Params[ 6 ].asTrack == nullptr ) { + event->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); + if( event->Params[ 6 ].asTrack == nullptr ) { ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exists" ); } } else { - Current->Params[ 6 ].asTrack = nullptr; + event->Params[ 6 ].asTrack = nullptr; } } else { - ErrorLog( "Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: copyvalues event \"" + event->asName + "\" cannot find memcell \"" + event->asNodeName + "\"" ); } - std::string const cellastext { Current->Params[ 9 ].asText }; - cell = simulation::Memory.find( Current->Params[ 9 ].asText ); // komórka źódłowa - SafeDeleteArray( Current->Params[ 9 ].asText ); // usunięcie nazwy komórki + std::string const cellastext { event->Params[ 9 ].asText }; + cell = simulation::Memory.find( event->Params[ 9 ].asText ); // komórka źódłowa + SafeDeleteArray( event->Params[ 9 ].asText ); // usunięcie nazwy komórki if( cell != nullptr ) { - Current->Params[ 8 ].asLocation = &( cell->location() ); - Current->Params[ 9 ].asMemCell = cell; // komórka źródłowa + event->Params[ 8 ].asLocation = &( cell->location() ); + event->Params[ 9 ].asMemCell = cell; // komórka źródłowa } else { - ErrorLog( "Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + cellastext + "\"" ); + ErrorLog( "Bad event: copyvalues event \"" + event->asName + "\" cannot find memcell \"" + cellastext + "\"" ); } break; } case tp_Animation: { // animacja modelu // retrieve target name parameter - std::string const cellastext = Current->Params[ 9 ].asText; - SafeDeleteArray( Current->Params[ 9 ].asText ); + std::string const cellastext = event->Params[ 9 ].asText; + SafeDeleteArray( event->Params[ 9 ].asText ); // egzemplarz modelu do animowania - auto *instance = simulation::Instances.find( Current->asNodeName ); + auto *instance = simulation::Instances.find( event->asNodeName ); if( instance != nullptr ) { - if( Current->Params[ 0 ].asInt == 4 ) { + if( event->Params[ 0 ].asInt == 4 ) { // model dla całomodelowych animacji - Current->Params[ 9 ].asModel = instance; + event->Params[ 9 ].asModel = instance; } else { // standardowo przypisanie submodelu - Current->Params[ 9 ].asAnimContainer = instance->GetContainer( cellastext ); // submodel - if( Current->Params[ 9 ].asAnimContainer ) { - Current->Params[ 9 ].asAnimContainer->WillBeAnimated(); // oflagowanie animacji - if( Current->Params[ 9 ].asAnimContainer->Event() == nullptr ) { + event->Params[ 9 ].asAnimContainer = instance->GetContainer( cellastext ); // submodel + if( event->Params[ 9 ].asAnimContainer ) { + event->Params[ 9 ].asAnimContainer->WillBeAnimated(); // oflagowanie animacji + if( event->Params[ 9 ].asAnimContainer->Event() == nullptr ) { // nie szukać, gdy znaleziony - Current->Params[ 9 ].asAnimContainer->EventAssign( - FindEvent( Current->asNodeName + "." + cellastext + ":done" ) ); + event->Params[ 9 ].asAnimContainer->EventAssign( + FindEvent( event->asNodeName + "." + cellastext + ":done" ) ); } } } } else { - ErrorLog( "Bad event: animation event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: animation event \"" + event->asName + "\" cannot find model instance \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Lights: { // zmiana świeteł modelu - auto *instance = simulation::Instances.find( Current->asNodeName ); + auto *instance = simulation::Instances.find( event->asNodeName ); if( instance != nullptr ) - Current->Params[ 9 ].asModel = instance; + event->Params[ 9 ].asModel = instance; else - ErrorLog( "Bad event: lights event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); - Current->asNodeName = ""; + ErrorLog( "Bad event: lights event \"" + event->asName + "\" cannot find model instance \"" + event->asNodeName + "\"" ); + event->asNodeName = ""; break; } case tp_Visible: { // ukrycie albo przywrócenie obiektu - editor::basic_node *node = simulation::Instances.find( Current->asNodeName ); // najpierw model + editor::basic_node *node = simulation::Instances.find( event->asNodeName ); // najpierw model if( node == nullptr ) { // albo tory? - node = simulation::Paths.find( Current->asNodeName ); + node = simulation::Paths.find( event->asNodeName ); } if( node == nullptr ) { // może druty? - node = simulation::Traction.find( Current->asNodeName ); + node = simulation::Traction.find( event->asNodeName ); } if( node != nullptr ) - Current->Params[ 9 ].asEditorNode = node; + event->Params[ 9 ].asEditorNode = node; else - ErrorLog( "Bad event: visibility event \"" + Current->asName + "\" cannot find item \"" + Current->asNodeName + "\"" ); - Current->asNodeName = ""; + ErrorLog( "Bad event: visibility event \"" + event->asName + "\" cannot find item \"" + event->asNodeName + "\"" ); + event->asNodeName = ""; break; } case tp_Switch: { // przełożenie zwrotnicy albo zmiana stanu obrotnicy - auto *track = simulation::Paths.find( Current->asNodeName ); + auto *track = simulation::Paths.find( event->asNodeName ); if( track != nullptr ) { // dowiązanie toru if( track->iAction == NULL ) { // jeśli nie jest zwrotnicą ani obrotnicą to będzie się zmieniał stan uszkodzenia track->iAction |= 0x100; } - Current->Params[ 9 ].asTrack = track; - if( ( Current->Params[ 0 ].asInt == 0 ) - && ( Current->Params[ 2 ].asdouble >= 0.0 ) ) { + event->Params[ 9 ].asTrack = track; + if( ( event->Params[ 0 ].asInt == 0 ) + && ( event->Params[ 2 ].asdouble >= 0.0 ) ) { // jeśli przełącza do stanu 0 & jeśli jest zdefiniowany dodatkowy ruch iglic // przesłanie parametrów - Current->Params[ 9 ].asTrack->Switch( - Current->Params[ 0 ].asInt, - Current->Params[ 1 ].asdouble, - Current->Params[ 2 ].asdouble ); + event->Params[ 9 ].asTrack->Switch( + event->Params[ 0 ].asInt, + event->Params[ 1 ].asdouble, + event->Params[ 2 ].asdouble ); } } else { - ErrorLog( "Bad event: switch event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: switch event \"" + event->asName + "\" cannot find track \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Sound: { // odtworzenie dźwięku - auto *sound = simulation::Sounds.find( Current->asNodeName ); + auto *sound = simulation::Sounds.find( event->asNodeName ); if( sound != nullptr ) - Current->Params[ 9 ].tsTextSound = sound; + event->Params[ 9 ].tsTextSound = sound; else - ErrorLog( "Bad event: sound event \"" + Current->asName + "\" cannot find static sound \"" + Current->asNodeName + "\"" ); - Current->asNodeName = ""; + ErrorLog( "Bad event: sound event \"" + event->asName + "\" cannot find static sound \"" + event->asNodeName + "\"" ); + event->asNodeName = ""; break; } case tp_TrackVel: { // ustawienie prędkości na torze - if( false == Current->asNodeName.empty() ) { - auto *track = simulation::Paths.find( Current->asNodeName ); + if( false == event->asNodeName.empty() ) { + auto *track = simulation::Paths.find( event->asNodeName ); if( track != nullptr ) { // flaga zmiany prędkości toru jest istotna dla skanowania track->iAction |= 0x200; - Current->Params[ 9 ].asTrack = track; + event->Params[ 9 ].asTrack = track; } else { - ErrorLog( "Bad event: track velocity event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: track velocity event \"" + event->asName + "\" cannot find track \"" + event->asNodeName + "\"" ); } } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_DynVel: { // komunikacja z pojazdem o konkretnej nazwie - if( Current->asNodeName == "activator" ) - Current->Params[ 9 ].asDynamic = nullptr; + if( event->asNodeName == "activator" ) + event->Params[ 9 ].asDynamic = nullptr; else { - auto *vehicle = simulation::Vehicles.find( Current->asNodeName ); + auto *vehicle = simulation::Vehicles.find( event->asNodeName ); if( vehicle != nullptr ) - Current->Params[ 9 ].asDynamic = vehicle; + event->Params[ 9 ].asDynamic = vehicle; else - Error( "Bad event: vehicle velocity event \"" + Current->asName + "\" cannot find vehicle \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: vehicle velocity event \"" + event->asName + "\" cannot find vehicle \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Multiple: { std::string cellastext; - if( Current->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora - cellastext = Current->Params[ 9 ].asText; - SafeDeleteArray( Current->Params[ 9 ].asText ); - Current->Params[ 9 ].asPointer = nullptr; // zerowanie wskaźnika, aby wykryć brak obeiktu + if( event->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora + cellastext = event->Params[ 9 ].asText; + SafeDeleteArray( event->Params[ 9 ].asText ); + event->Params[ 9 ].asPointer = nullptr; // zerowanie wskaźnika, aby wykryć brak obeiktu } - if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { + if( event->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { // jeśli chodzi o zajetosc toru - Current->Params[ 9 ].asTrack = simulation::Paths.find( cellastext ); - if( Current->Params[ 9 ].asTrack == nullptr ) { - ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find track \"" + cellastext + "\"" ); - Current->iFlags &= ~( conditional_trackoccupied | conditional_trackfree ); // zerowanie flag + event->Params[ 9 ].asTrack = simulation::Paths.find( cellastext ); + if( event->Params[ 9 ].asTrack == nullptr ) { + ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find track \"" + cellastext + "\"" ); + event->iFlags &= ~( conditional_trackoccupied | conditional_trackfree ); // zerowanie flag } } - else if( Current->iFlags & ( conditional_memstring | conditional_memval1 | conditional_memval2 ) ) { + else if( event->iFlags & ( conditional_memstring | conditional_memval1 | conditional_memval2 ) ) { // jeśli chodzi o komorke pamieciową - Current->Params[ 9 ].asMemCell = simulation::Memory.find( cellastext ); - if( Current->Params[ 9 ].asMemCell == nullptr ) { - ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find memory cell \"" + cellastext + "\"" ); - Current->iFlags &= ~( conditional_memstring | conditional_memval1 | conditional_memval2 ); + event->Params[ 9 ].asMemCell = simulation::Memory.find( cellastext ); + if( event->Params[ 9 ].asMemCell == nullptr ) { + ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find memory cell \"" + cellastext + "\"" ); + event->iFlags &= ~( conditional_memstring | conditional_memval1 | conditional_memval2 ); } } for( int i = 0; i < 8; ++i ) { - if( Current->Params[ i ].asText != nullptr ) { - cellastext = Current->Params[ i ].asText; - SafeDeleteArray( Current->Params[ i ].asText ); - Current->Params[ i ].asEvent = FindEvent( cellastext ); - if( Current->Params[ i ].asEvent == nullptr ) { + if( event->Params[ i ].asText != nullptr ) { + cellastext = event->Params[ i ].asText; + SafeDeleteArray( event->Params[ i ].asText ); + event->Params[ i ].asEvent = FindEvent( cellastext ); + if( event->Params[ i ].asEvent == nullptr ) { // Ra: tylko w logu informacja o braku - ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find event \"" + cellastext + "\"" ); + ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find event \"" + cellastext + "\"" ); } } } @@ -1537,14 +1575,14 @@ event_manager::InitEvents() { } case tp_Voltage: { // zmiana napięcia w zasilaczu (TractionPowerSource) - if( false == Current->asNodeName.empty() ) { - auto *powersource = simulation::Powergrid.find( Current->asNodeName ); // podłączenie zasilacza + if( false == event->asNodeName.empty() ) { + auto *powersource = simulation::Powergrid.find( event->asNodeName ); // podłączenie zasilacza if( powersource != nullptr ) - Current->Params[ 9 ].psPower = powersource; + event->Params[ 9 ].psPower = powersource; else - ErrorLog( "Bad event: voltage event \"" + Current->asName + "\" cannot find power source \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: voltage event \"" + event->asName + "\" cannot find power source \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Message: { @@ -1554,7 +1592,37 @@ event_manager::InitEvents() { } // switch - if( Current->fDelay < 0 ) { AddToQuery( Current, nullptr ); } + if( event->fDelay < 0 ) { AddToQuery( event, nullptr ); } + } +} + +// legacy method, initializes event launchers after deserialization from scenario file +void +event_manager::InitLaunchers() { + + for( auto *launcher : m_launchers.sequence() ) { + + if( launcher->iCheckMask != 0 ) { + if( launcher->asMemCellName != "none" ) { + // jeśli jest powiązana komórka pamięci + launcher->MemCell = simulation::Memory.find( launcher->asMemCellName ); + if( launcher->MemCell == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find memcell \"" + launcher->asMemCellName + "\"" ); + } + } + else { + launcher->MemCell = nullptr; + } + } + + launcher->Event1 = ( + launcher->asEvent1Name != "none" ? + simulation::Events.FindEvent( launcher->asEvent1Name ) : + nullptr ); + launcher->Event2 = ( + launcher->asEvent2Name != "none" ? + simulation::Events.FindEvent( launcher->asEvent2Name ) : + nullptr ); } } diff --git a/Event.h b/Event.h index cd0f89cd..ad32a5b0 100644 --- a/Event.h +++ b/Event.h @@ -63,7 +63,9 @@ union TParam { void *asPointer; TMemCell *asMemCell; +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *nGroundNode; +#endif editor::basic_node *asEditorNode; glm::dvec3 const *asLocation; TTrack *asTrack; @@ -125,6 +127,12 @@ public: // destructor ~event_manager(); // methods + // adds specified event launcher to the list of global launchers + void + queue( TEventLauncher *Launcher ); + // legacy method, updates event queues + void + update(); // adds provided event to the collection. returns: true on success // TBD, TODO: return handle to the event bool @@ -144,6 +152,9 @@ public: // legacy method, initializes events after deserialization from scenario file void InitEvents(); + // legacy method, initializes event launchers after deserialization from scenario file + void + InitLaunchers(); private: // types diff --git a/Ground.cpp b/Ground.cpp index ac467df8..4ccef2ef 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -190,7 +190,7 @@ void TGroundNode::RenderHidden() } return; case TP_EVLAUNCH: - if (EvLaunch->Render()) + if (EvLaunch->check_conditions()) if ((EvLaunch->dRadius < 0) || (mgn < EvLaunch->dRadius)) { WriteLog("Eventlauncher " + asName); @@ -1006,7 +1006,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } break; -#endif case TP_EVLAUNCH: parser->getTokens(3); *parser @@ -1018,7 +1017,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->EvLaunch = new TEventLauncher(); tmp->EvLaunch->Load(parser); break; -#ifdef EU07_USE_OLD_GROUNDCODE case TP_TRACK: tmp->pTrack = new TTrack( tmp->asName ); if (Global::iWriteLogEnabled & 4) @@ -1600,7 +1598,6 @@ TEvent * TGround::FindEvent(const std::string &asEventName) lookup->second : nullptr ); } -#endif void TGround::FirstInit() { // ustalanie zależności na scenerii przed wczytaniem pojazdów @@ -1668,18 +1665,14 @@ void TGround::FirstInit() WriteLog("InitNormals OK"); -#ifdef EU07_USE_OLD_GROUNDCODE InitTracks(); //łączenie odcinków ze sobą i przyklejanie eventów WriteLog("InitTracks OK"); InitTraction(); //łączenie drutów ze sobą WriteLog("InitTraction OK"); InitEvents(); WriteLog( "InitEvents OK" ); -#endif - InitLaunchers(); WriteLog("InitLaunchers OK"); - WriteLog("FirstInit is done"); }; @@ -1839,7 +1832,6 @@ bool TGround::Init(std::string File) } else if (str == "event") { -#ifdef EU07_USE_OLD_GROUNDCODE TEvent *tmp = new TEvent(); tmp->Load(&parser, pOrigin); if (tmp->Type == tp_Unknown) @@ -1904,7 +1896,6 @@ bool TGround::Init(std::string File) } } } -#endif } else if (str == "rotate") { @@ -2126,14 +2117,13 @@ bool TGround::Init(std::string File) if (!bInitDone) FirstInit(); // jeśli nie było w scenerii -#ifdef EU07_USE_OLD_TERRAINCODE + if (Global::pTerrainCompact) TerrainWrite(); // Ra: teraz można zapisać teren w jednym pliku -#endif Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; } -#ifdef EU07_USE_OLD_GROUNDCODE + bool TGround::InitEvents() { //łączenie eventów z pozostałymi obiektami TGroundNode *tmp, *trk; @@ -2816,7 +2806,7 @@ void TGround::TrackJoin(TGroundNode *Current) } } } - +#ifdef EU07_USE_OLD_GROUNDCODE // McZapkie-070602: wyzwalacze zdarzen bool TGround::InitLaunchers() { @@ -2837,26 +2827,16 @@ bool TGround::InitLaunchers() } else EventLauncher->MemCell = nullptr; -#ifdef EU07_USE_OLD_GROUNDCODE EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? FindEvent(EventLauncher->asEvent1Name) : nullptr; EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? FindEvent(EventLauncher->asEvent2Name) : nullptr; -#else - EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? - simulation::Events.FindEvent(EventLauncher->asEvent1Name) : - nullptr; - EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? - simulation::Events.FindEvent(EventLauncher->asEvent2Name) : - nullptr; -#endif } return true; } -#ifdef EU07_USE_OLD_GROUNDCODE TTrack * TGround::FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude) { // wyszukiwanie innego toru kończącego się w (Point) TTrack *tmp; @@ -3539,7 +3519,7 @@ TGround::Update_Hidden() { } } } -#endif + // Winger 170204 - szukanie trakcji nad pantografami bool TGround::GetTraction(TDynamicObject *model) { // aktualizacja drutu zasilającego dla każdego pantografu, żeby odczytać napięcie @@ -3774,7 +3754,7 @@ bool TGround::GetTraction(TDynamicObject *model) return true; }; - +#endif #ifdef _WINDOWS //--------------------------------------------------------------------------- void TGround::Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) diff --git a/Ground.h b/Ground.h index 00bf072b..a6ef54c7 100644 --- a/Ground.h +++ b/Ground.h @@ -277,15 +277,13 @@ class TGround TGround(); ~TGround(); void Free(); +#ifdef EU07_USE_OLD_GROUNDCODE bool Init( std::string File ); void FirstInit(); -#ifdef EU07_USE_OLD_GROUNDCODE void InitTracks(); void InitTraction(); bool InitEvents(); -#endif bool InitLaunchers(); -#ifdef EU07_USE_OLD_GROUNDCODE TTrack * FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude); TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TGroundNode *Exclude); TTraction * TractionNearestFind(glm::dvec3 &p, int dir, TGroundNode *n); @@ -295,9 +293,7 @@ class TGround void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS void Update_Hidden(); // updates invisible elements of the scene -#endif bool GetTraction(TDynamicObject *model); -#ifdef EU07_USE_OLD_GROUNDCODE bool AddToQuery( TEvent *Event, TDynamicObject *Node ); bool CheckQuery(); TGroundNode * DynamicFindAny(std::string const &Name); diff --git a/MemCell.cpp b/MemCell.cpp index 8143b2fa..dcbba49c 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -129,7 +129,11 @@ bool TMemCell::Load(cParser *parser) return true; } +#ifdef EU07_USE_OLD_GROUNDCODE void TMemCell::PutCommand(TController *Mech, vector3 *Loc) +#else +void TMemCell::PutCommand( TController *Mech, glm::dvec3 const *Loc ) +#endif { // wysłanie zawartości komórki do AI if (Mech) Mech->PutCommand(szText, fValue1, fValue2, Loc); diff --git a/MemCell.h b/MemCell.h index aa84d3b9..0364eb89 100644 --- a/MemCell.h +++ b/MemCell.h @@ -37,7 +37,11 @@ class TMemCell : public editor::basic_node { bool Load(cParser *parser); void +#ifdef EU07_USE_OLD_GROUNDCODE PutCommand( TController *Mech, Math3D::vector3 *Loc ); +#else + PutCommand( TController *Mech, glm::dvec3 const *Loc ); +#endif bool Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask ); std::string const & diff --git a/Names.h b/Names.h index 5f76306a..1b338531 100644 --- a/Names.h +++ b/Names.h @@ -108,4 +108,11 @@ protected: // members type_sequence m_items; type_map m_itemmap; + +public: + // data access + typename type_sequence & + sequence() { + return m_items; } + }; diff --git a/Timer.cpp b/Timer.cpp index e78b50a7..dcfb6780 100644 --- a/Timer.cpp +++ b/Timer.cpp @@ -11,8 +11,9 @@ http://mozilla.org/MPL/2.0/. #include "Timer.h" #include "Globals.h" -namespace Timer -{ +namespace Timer { + +subsystem_stopwatches subsystem; double DeltaTime, DeltaRenderTime; double fFPS{ 0.0f }; diff --git a/Timer.h b/Timer.h index b6b58371..d0d9e5c9 100644 --- a/Timer.h +++ b/Timer.h @@ -9,8 +9,7 @@ http://mozilla.org/MPL/2.0/. #pragma once -namespace Timer -{ +namespace Timer { double GetTime(); @@ -28,6 +27,39 @@ double GetFPS(); void ResetTimers(); void UpdateTimers(bool pause); + +class stopwatch { + +public: + void + start() { + m_start = std::chrono::steady_clock::now(); } + void + stop() { + m_accumulator = 0.95f * m_accumulator + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - m_start ) ).count() / 1000.f; } + float + average() const { + return m_accumulator / 20.f;} + +private: + std::chrono::time_point m_start { std::chrono::steady_clock::now() }; + float m_accumulator { 1000.f / 30.f * 20.f }; // 20 last samples, initial 'neutral' rate of 30 fps +}; + +struct subsystem_stopwatches { + stopwatch gfx_total; + stopwatch gfx_color; + stopwatch gfx_shadows; + stopwatch gfx_reflections; + stopwatch gfx_swap; + stopwatch sim_total; + stopwatch sim_dynamics; + stopwatch sim_events; + stopwatch sim_ai; +}; + +extern subsystem_stopwatches subsystem; + }; //--------------------------------------------------------------------------- diff --git a/World.cpp b/World.cpp index be3f6b32..c02db34d 100644 --- a/World.cpp +++ b/World.cpp @@ -953,6 +953,7 @@ void TWorld::FollowView(bool wycisz) { bool TWorld::Update() { Timer::UpdateTimers(Global::iPause != 0); + Timer::subsystem.sim_total.start(); if( (Global::iPause == false) || (m_init == false) ) { @@ -1026,6 +1027,7 @@ bool TWorld::Update() { // 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(); #ifdef EU07_USE_OLD_GROUNDCODE if( true == Global::FullPhysics ) { // mixed calculation mode, steps calculated in ~0.05s chunks @@ -1067,6 +1069,7 @@ bool TWorld::Update() { simulation::State.update( stepdeltatime, updatecount ); } #endif + Timer::subsystem.sim_dynamics.stop(); // secondary fixed step simulation time routines while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) { @@ -1110,7 +1113,7 @@ bool TWorld::Update() { Ground.CheckQuery(); Ground.Update_Hidden(); #else - simulation::Events.CheckQuery(); + simulation::Events.update(); simulation::Region->update(); #endif @@ -1144,6 +1147,8 @@ bool TWorld::Update() { Update_Camera( dt ); + Timer::subsystem.sim_total.stop(); + GfxRenderer.Update( dt ); ResourceSweep(); @@ -1659,20 +1664,14 @@ TWorld::Update_UI() { "VBO" : "Display Lists" ) + " "; - // dump last opengl error, if any - GLenum glerror = ::glGetError(); - if( glerror != GL_NO_ERROR ) { - std::string glerrorstring( (char *)::gluErrorString( glerror ) ); - win1250_to_ascii( glerrorstring ); - Global::LastGLError = std::to_string( glerror ) + " (" + glerrorstring + ")"; - } if( false == Global::LastGLError.empty() ) { uitextline2 += "Last openGL error: " + Global::LastGLError; } // renderer stats - uitextline3 = GfxRenderer.Info(); + uitextline3 = GfxRenderer.info_times(); + uitextline4 = GfxRenderer.info_stats(); break; } @@ -1683,6 +1682,9 @@ TWorld::Update_UI() { 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"; break; } diff --git a/renderer.cpp b/renderer.cpp index 3107a28e..e875f081 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -315,23 +315,32 @@ opengl_renderer::Init( GLFWwindow *Window ) { bool opengl_renderer::Render() { - if( m_drawstart != std::chrono::steady_clock::time_point() ) { - m_drawtime = std::max( 20.f, 0.95f * m_drawtime + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - m_drawstart ) ).count() / 1000.f ); - } - m_drawstart = std::chrono::steady_clock::now(); - auto const drawstartcolorpass = m_drawstart; + Timer::subsystem.gfx_total.stop(); + Timer::subsystem.gfx_total.start(); // note: gfx_total is actually frame total, clean this up + Timer::subsystem.gfx_color.start(); m_renderpass.draw_mode = rendermode::none; // force setup anew - m_debuginfo.clear(); - ++m_framestamp; + m_debugtimestext.clear(); + m_debugstats = debug_stats(); Render_pass( rendermode::color ); + Timer::subsystem.gfx_color.stop(); + + Timer::subsystem.gfx_swap.start(); + glfwSwapBuffers( m_window ); + Timer::subsystem.gfx_swap.stop(); m_drawcount = m_cellqueue.size(); - // accumulate last 20 frames worth of render time (cap at 1000 fps to prevent calculations going awry) - m_drawtimecolorpass = std::max( 20.f, 0.95f * m_drawtimecolorpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstartcolorpass ) ).count() / 1000.f ); - m_debuginfo += "frame total: " + to_string( m_drawtimecolorpass / 20.f, 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; + m_debugtimestext + += "frame: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) " + += "gpu side: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " msec " + += "(" + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " msec total)"; + m_debugstatstext = + "drawcalls: " + to_string( m_debugstats.drawcalls ) + + "; dyn: " + to_string( m_debugstats.dynamics ) + " mod: " + to_string( m_debugstats.models ) + " sub: " + to_string( m_debugstats.submodels ) + + "; trk: " + to_string( m_debugstats.paths ) + " shp: " + to_string( m_debugstats.shapes ) + + " trc: " + to_string( m_debugstats.traction ) + " lin: " + to_string( m_debugstats.lines ); - glfwSwapBuffers( m_window ); + ++m_framestamp; return true; // for now always succeed } @@ -464,7 +473,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup - auto const shadowdrawstart = std::chrono::steady_clock::now(); + Timer::subsystem.gfx_shadows.start(); ::glBindFramebufferEXT( GL_FRAMEBUFFER, m_shadowframebuffer ); @@ -500,9 +509,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glDisable( GL_SCISSOR_TEST ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target - - m_drawtimeshadowpass = 0.95f * m_drawtimeshadowpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - shadowdrawstart ) ).count() / 1000.f; - m_debuginfo += "shadows: " + to_string( m_drawtimeshadowpass / 20.f, 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; + Timer::subsystem.gfx_shadows.stop(); + m_debugtimestext += "shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; } break; } @@ -602,7 +610,7 @@ bool opengl_renderer::Render_reflections() { auto const &time = simulation::Time.data(); - auto const timestamp = time.wDay * 60 * 24 + time.wHour * 60 + time.wMinute; + auto const timestamp = time.wDay * 24 * 60 + time.wHour * 60 + time.wMinute; if( ( timestamp - m_environmentupdatetime < 5 ) && ( glm::length( m_renderpass.camera.position() - m_environmentupdatelocation ) < 1000.0 ) ) { // run update every 5+ mins of simulation time, or at least 1km from the last location @@ -1477,12 +1485,6 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { break; } } -#ifdef EU07_USE_OLD_TERRAINCODE - if( Groundcell->nTerrain ) { - - Render( Groundcell->nTerrain ); - } -#endif // add the subcells of the cell to the draw queue switch( m_renderpass.draw_mode ) { @@ -1645,16 +1647,7 @@ opengl_renderer::Render( scene::basic_region *Region ) { Update_Lights( simulation::Lights ); Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); -/* - // draw queue was filled while rendering content of ground cells. now sort the nodes based on their distance to viewer... - // TODO: move sorting for translucent phase, for opaque geometry render cells in initial order to reduce vbo switching - std::sort( - std::begin( m_cellqueue ), - std::end( m_cellqueue ), - []( distancecell_pair const &Left, distancecell_pair const &Right ) { - return ( Left.first < Right.first ); } ); -*/ - // ...then render the opaque content of the visible cells. + // draw queue is filled while rendering sections Render( std::begin( m_cellqueue ), std::end( m_cellqueue ) ); break; } @@ -1749,7 +1742,7 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite glEnable( GL_LIGHTING ); #endif // shapes - for( auto const &shape : section->m_shapes ) { Render( shape ); } + for( auto const &shape : section->m_shapes ) { Render( shape, true ); } // post-render cleanup ::glPopMatrix(); } @@ -1852,7 +1845,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator glEnable( GL_LIGHTING ); #endif // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks // TODO: update after path node refactoring for( auto *path : cell->m_paths ) { Render( path ); } @@ -1871,7 +1864,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // render // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // TODO: add other content types // post-render cleanup @@ -1887,7 +1880,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks // TODO: add path to the node picking list for( auto *path : cell->m_paths ) { Render( path ); } @@ -1922,6 +1915,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator Render( dynamic ); } } + break; } case rendermode::pickscenery: { // opaque parts of instanced models @@ -1944,9 +1938,46 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator } void -opengl_renderer::Render( scene::shape_node const &Shape ) { +opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange ) { +/* + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Node->fSquareMinRadius ) + || ( distancesquared >= Node->fSquareRadius ) ) { + return false; + } +*/ + auto const &data{ Shape.data() }; + + if( false == Ignorerange ) { + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( data.area.center - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( data.area.center - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < data.rangesquared_min ) + || ( distancesquared >= data.rangesquared_max ) ) { + return; + } + } - auto const &data { Shape.data() }; // setup Bind_Material( data.material ); switch( m_renderpass.draw_mode ) { @@ -1971,6 +2002,9 @@ opengl_renderer::Render( scene::shape_node const &Shape ) { } // render m_geometry.draw( data.geometry ); + // debug data + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; } void @@ -2021,19 +2055,7 @@ opengl_renderer::Render( TAnimModel *Instance ) { bool opengl_renderer::Render( TGroundNode *Node ) { -#ifdef EU07_USE_OLD_TERRAINCODE - switch (Node->iType) - { // obiekty renderowane niezależnie od odległości - case TP_SUBMODEL: - ::glPushMatrix(); - auto const originoffset = Node->pCenter - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - TSubModel::fSquareDist = 0; - Render( Node->smTerrain ); - ::glPopMatrix(); - return true; - } -#endif + double distancesquared; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { @@ -2073,6 +2095,9 @@ opengl_renderer::Render( TGroundNode *Node ) { ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render Render( Node->pTrack ); + // debug + ++m_debugstats.paths; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); return true; @@ -2155,7 +2180,9 @@ opengl_renderer::Render( TGroundNode *Node ) { } // render m_geometry.draw( Node->Piece->geometry ); - + // debug +// ++m_debugstats.lines; +// ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -2201,6 +2228,9 @@ opengl_renderer::Render( TGroundNode *Node ) { } // render m_geometry.draw( Node->Piece->geometry ); + // debug + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -2236,6 +2266,9 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { if( false == Dynamic->renderme ) { return false; } + // debug data + ++m_debugstats.dynamics; + // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); @@ -2437,6 +2470,9 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, float c // render Render( Model->Root ); + // debug data + ++m_debugstats.models; + // post-render cleanup return true; @@ -2468,6 +2504,10 @@ opengl_renderer::Render( TSubModel *Submodel ) { && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { + // debug data + ++m_debugstats.submodels; + ++m_debugstats.drawcalls; + if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); if( Submodel->fMatrix ) @@ -2717,6 +2757,9 @@ opengl_renderer::Render( TTrack *Track ) { return; } + ++m_debugstats.paths; + ++m_debugstats.drawcalls; + switch( m_renderpass.draw_mode ) { case rendermode::color: case rendermode::reflections: { @@ -2867,7 +2910,7 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render // NOTE: we can reuse the method used to draw opaque geometry - for( auto const &shape : cell->m_shapestranslucent ) { Render( shape ); } + for( auto const &shape : cell->m_shapestranslucent ) { Render( shape, false ); } // post-render cleanup ::glPopMatrix(); } @@ -3003,6 +3046,9 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { // render m_geometry.draw( Traction->m_geometry ); + // debug data + ++m_debugstats.traction; + ++m_debugstats.drawcalls; } #endif bool @@ -3056,6 +3102,9 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->hvTraction->m_geometry ); + // debug data + ++m_debugstats.traction; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3116,6 +3165,8 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->Piece->geometry ); +// ++m_debugstats.lines; +// ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3141,7 +3192,9 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->Piece->geometry ); - + // debug data + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3285,6 +3338,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { + // debug data + ++m_debugstats.submodels; + ++m_debugstats.drawcalls; + if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); if( Submodel->fMatrix ) @@ -3580,10 +3637,10 @@ opengl_renderer::Update( double const Deltatime ) { } m_updateaccumulator = 0.0; - m_framerate = 1000.f / ( m_drawtime / 20.f ); + m_framerate = 1000.f / ( Timer::subsystem.gfx_total.average() ); // adjust draw ranges etc, based on recent performance - auto const framerate = 1000.f / (m_drawtimecolorpass / 20.f); + auto const framerate = 1000.f / Timer::subsystem.gfx_color.average(); float targetfactor; if( framerate > 90.0 ) { targetfactor = 3.0f; } @@ -3620,7 +3677,7 @@ opengl_renderer::Update( double const Deltatime ) { } if( true == DebugModeFlag ) { - m_debuginfo += m_textures.info(); + m_debugtimestext += m_textures.info(); } if( ( true == Global::ControlPicking ) @@ -3639,13 +3696,26 @@ opengl_renderer::Update( double const Deltatime ) { else { m_picksceneryitem = nullptr; } -}; + // dump last opengl error, if any + auto const glerror = ::glGetError(); + if( glerror != GL_NO_ERROR ) { + std::string glerrorstring( ( char * )::gluErrorString( glerror ) ); + win1250_to_ascii( glerrorstring ); + Global::LastGLError = std::to_string( glerror ) + " (" + glerrorstring + ")"; + } +} // debug performance string std::string const & -opengl_renderer::Info() const { +opengl_renderer::info_times() const { - return m_debuginfo; + return m_debugtimestext; +} + +std::string const & +opengl_renderer::info_stats() const { + + return m_debugstatstext; } void diff --git a/renderer.h b/renderer.h index f5e888ad..9879fb93 100644 --- a/renderer.h +++ b/renderer.h @@ -203,7 +203,9 @@ public: Update_Pick_Node(); // debug performance string std::string const & - Info() const; + info_times() const; + std::string const & + info_stats() const; // members GLenum static const sunlight{ GL_LIGHT0 }; @@ -227,6 +229,17 @@ private: diffuse }; + struct debug_stats { + int dynamics { 0 }; + int models { 0 }; + int submodels { 0 }; + int paths { 0 }; + int traction { 0 }; + int shapes { 0 }; + int lines { 0 }; + int drawcalls { 0 }; + }; + #ifdef EU07_USE_OLD_GROUNDCODE using distancesubcell_pair = std::pair< double, TSubRect * >; #else @@ -289,7 +302,7 @@ private: void Render( cell_sequence::iterator First, cell_sequence::iterator Last ); void - Render( scene::shape_node const &Shape ); + Render( scene::shape_node const &Shape, bool const Ignorerange ); void Render( TAnimModel *Instance ); #endif @@ -383,14 +396,12 @@ private: units_state m_unitstate; unsigned int m_framestamp; // id of currently rendered gfx frame - float m_drawtime { 1000.f / 30.f * 20.f }; // start with presumed 'neutral' average of 30 fps - std::chrono::steady_clock::time_point m_drawstart; // cached start time of previous frame float m_framerate; - float m_drawtimecolorpass { 1000.f / 30.f * 20.f }; - float m_drawtimeshadowpass { 0.f }; double m_updateaccumulator { 0.0 }; - std::string m_debuginfo; + std::string m_debugtimestext; std::string m_pickdebuginfo; + debug_stats m_debugstats; + std::string m_debugstatstext; glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f }; glm::vec4 m_shadowcolor { 0.65f, 0.65f, 0.65f, 1.f }; diff --git a/scene.cpp b/scene.cpp index 2dfb70da..2fa1783d 100644 --- a/scene.cpp +++ b/scene.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "scene.h" #include "globals.h" +#include "timer.h" #include "renderer.h" #include "logs.h" @@ -19,12 +20,32 @@ namespace scene { // legacy method, updates sounds and polls event launchers within radius around specified point void basic_cell::update() { -/* - // renderowanie obiektów aktywnych a niewidocznych - for( auto node = subcell->nRenderHidden; node; node = node->nNext3 ) { - node->RenderHidden(); + + // sounds + auto const deltatime = Timer::GetDeltaTime(); + for( auto *sound : m_sounds ) { + + if( ( sound->GetStatus() & DSBSTATUS_PLAYING ) == DSBPLAY_LOOPING ) { + sound->Play( 1, DSBPLAY_LOOPING, true, sound->vSoundPosition ); + sound->AdjFreq( 1.0, deltatime ); + } } -*/ + // event launchers + for( auto *launcher : m_eventlaunchers ) { + if( ( true == launcher->check_conditions() ) + && ( SquareMagnitude( launcher->location() - Global::pCameraPosition ) < launcher->dRadius ) ) { + + WriteLog( "Eventlauncher " + launcher->name() ); + if( ( true == Global::shiftState ) + && ( launcher->Event2 != nullptr ) ) { + simulation::Events.AddToQuery( launcher->Event2, nullptr ); + } + else if( launcher->Event1 ) { + simulation::Events.AddToQuery( launcher->Event1, nullptr ); + } + } + } + // TBD, TODO: move to sound renderer for( auto *path : m_paths ) { // dźwięki pojazdów, również niewidocznych @@ -32,6 +53,83 @@ basic_cell::update() { } } +// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle +void +basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { + // Winger 170204 - szukanie trakcji nad pantografami + auto const vFront = glm::make_vec3( Vehicle->VectorFront().getArray() ); // wektor normalny dla płaszczyzny ruchu pantografu + auto const vUp = glm::make_vec3( Vehicle->VectorUp().getArray() ); // wektor pionu pudła (pochylony od pionu na przechyłce) + auto const vLeft = glm::make_vec3( Vehicle->VectorLeft().getArray() ); // wektor odległości w bok (odchylony od poziomu na przechyłce) + auto const position = glm::dvec3 { Vehicle->GetPosition() }; // współrzędne środka pojazdu + + auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; + auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); + + for( auto *traction : m_traction ) { + + // współczynniki równania parametrycznego + auto const paramfrontdot = glm::dot( traction->vParametric, vFront ); + auto const fRaParam = + -( glm::dot( traction->pPoint1, vFront ) - glm::dot( pantographposition, vFront ) ) + / ( paramfrontdot != 0.0 ? + paramfrontdot : + 0.001 ); // div0 trap + + if( ( fRaParam < -0.001 ) + || ( fRaParam > 1.001 ) ) { continue; } + // jeśli tylko jest w przedziale, wyznaczyć odległość wzdłuż wektorów vUp i vLeft + // punkt styku płaszczyzny z drutem (dla generatora łuku el.) + auto const vStyk = traction->pPoint1 + fRaParam * traction->vParametric; + // wektor musi się mieścić w przedziale ruchu pantografu + auto const vGdzie = vStyk - pantographposition; + auto fVertical = glm::dot( vGdzie, vUp ); + if( fVertical >= 0.0 ) { + // jeśli ponad pantografem (bo może łapać druty spod wiaduktu) + auto const fHorizontal = std::abs( glm::dot( vGdzie, vLeft ) ) - pantograph->fWidth; + + if( ( Global::bEnableTraction ) + && ( fVertical < pantograph->PantWys - 0.15 ) ) { + // jeśli drut jest niżej niż 15cm pod ślizgiem przełączamy w tryb połamania, o ile jedzie; + // (bEnableTraction) aby dało się jeździć na koślawych sceneriach + // i do tego jeszcze wejdzie pod ślizg + if( fHorizontal <= 0.0 ) { + // 0.635 dla AKP-1 AKP-4E + pantograph->PantWys = -1.0; // ujemna liczba oznacza połamanie + pantograph->hvPowerWire = nullptr; // bo inaczej się zasila w nieskończoność z połamanego + if( Vehicle->MoverParameters->EnginePowerSource.CollectorParameters.CollectorsNo > 0 ) { + // liczba pantografów teraz będzie mniejsza + --Vehicle->MoverParameters->EnginePowerSource.CollectorParameters.CollectorsNo; + } + if( DebugModeFlag ) { + ErrorLog( "Bad traction: " + Vehicle->name() + " broke pantograph at " + to_string( pantographposition ) ); + } + } + } + else if( fVertical < pantograph->PantTraction ) { + // ale niżej, niż poprzednio znaleziony + if( fHorizontal <= 0.0 ) { + // 0.635 dla AKP-1 AKP-4E + // to się musi mieścić w przedziale zaleznym od szerokości pantografu + pantograph->hvPowerWire = traction; // jakiś znaleziony + pantograph->PantTraction = fVertical; // zapamiętanie nowej wysokości + } + else if( fHorizontal < pantograph->fWidthExtra ) { + // czy zmieścił się w zakresie nabieżnika? problem jest, gdy nowy drut jest wyżej, + // wtedy pantograf odłącza się od starego, a na podniesienie do nowego potrzebuje czasu + // korekta wysokości o nabieżnik - drut nad nabieżnikiem jest geometrycznie jakby nieco wyżej + fVertical += 0.15 * fHorizontal / pantograph->fWidthExtra; + if( fVertical < pantograph->PantTraction ) { + // gdy po korekcie jest niżej, niż poprzednio znaleziony + // gdyby to wystarczyło, to możemy go uznać + pantograph->hvPowerWire = traction; // może być + pantograph->PantTraction = fVertical; // na razie liniowo na nabieżniku, dokładność poprawi się później + } + } + } + } + } +} + // adds provided shape to the cell void basic_cell::insert( shape_node Shape ) { @@ -107,18 +205,48 @@ basic_cell::insert( TAnimModel *Instance ) { } } +// adds provided sound instance to the cell +void +basic_cell::insert( TTextSound *Sound ) { + + m_active = true; + + m_sounds.emplace_back( Sound ); +} + +// adds provided sound instance to the cell +void +basic_cell::insert( TEventLauncher *Launcher ) { + + m_active = true; + + m_eventlaunchers.emplace_back( Launcher ); +} + // registers provided path in the lookup directory of the cell void basic_cell::register_end( TTrack *Path ) { - m_directories.paths.emplace( Path ); + m_directories.paths.emplace_back( Path ); + // eliminate potential duplicates + m_directories.paths.erase( + std::unique( + std::begin( m_directories.paths ), + std::end( m_directories.paths ) ), + std::end( m_directories.paths ) ); } // registers provided traction piece in the lookup directory of the cell void basic_cell::register_end( TTraction *Traction ) { - m_directories.traction.emplace( Traction ); + m_directories.traction.emplace_back( Traction ); + // eliminate potential duplicates + m_directories.traction.erase( + std::unique( + std::begin( m_directories.traction ), + std::end( m_directories.traction ) ), + std::end( m_directories.traction ) ); } // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance @@ -263,6 +391,29 @@ basic_section::update( glm::dvec3 const &Location, float const Radius ) { } } +// legacy method, finds and assigns traction piece(s) to pantographs of provided vehicle +void +basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { + + auto const vFront = glm::make_vec3( Vehicle->VectorFront().getArray() ); // wektor normalny dla płaszczyzny ruchu pantografu + auto const vUp = glm::make_vec3( Vehicle->VectorUp().getArray() ); // wektor pionu pudła (pochylony od pionu na przechyłce) + auto const vLeft = glm::make_vec3( Vehicle->VectorLeft().getArray() ); // wektor odległości w bok (odchylony od poziomu na przechyłce) + auto const position = glm::dvec3{ Vehicle->GetPosition() }; // współrzędne środka pojazdu + + auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; + auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); + + auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) }; + + for( auto &cell : m_cells ) { + // we reject early cells which aren't within our area of interest + if( glm::length2( cell.area().center - pantographposition ) < squaredradii ) { + cell.update_traction( Vehicle, Pantographindex ); + } + } +} + // adds provided shape to the section void basic_section::insert( shape_node Shape ) { @@ -289,47 +440,6 @@ basic_section::insert( shape_node Shape ) { } } -// adds provided path to the section -void -basic_section::insert( TTrack *Path ) { - - // pass the node to the appropriate partitioning cell - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - cell( Path->location() ).insert( Path ); -} - -// adds provided path to the section -void -basic_section::insert( TTraction *Traction ) { - - // pass the node to the appropriate partitioning cell - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - cell( Traction->location() ).insert( Traction ); -} - -// adds provided model instance to the section -void -basic_section::insert( TAnimModel *Instance ) { - - // pass the node to the appropriate partitioning cell - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - cell( Instance->location() ).insert( Instance ); -} - -// registers specified end point of the provided path in the lookup directory of the region -void -basic_section::register_end( TTrack *Path, glm::dvec3 const &Point ) { - - cell( Point ).register_end( Path ); -} - -// registers specified end point of the provided traction piece in the lookup directory of the region -void -basic_section::register_end( TTraction *Traction, glm::dvec3 const &Point ) { - - cell( Point ).register_end( Traction ); -} - // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { @@ -467,26 +577,11 @@ basic_section::cell( glm::dvec3 const &Location ) { basic_region::basic_region() { m_sections.fill( nullptr ); -/* - // initialize centers of sections: - // calculate center of 'top left' region section... - auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2; - glm::dvec3 regioncornercenter { centeroffset, 0, centeroffset }; - auto row { 0 }, column { 0 }; - // ...move through section array assigning centers left to right, front/top to back/bottom - for( auto §ion : m_sections ) { - section.center( regioncornercenter + glm::dvec3{ column * EU07_SECTIONSIZE, 0.0, row * EU07_SECTIONSIZE } ); - if( ++column >= EU07_REGIONSIDESECTIONCOUNT ) { - ++row; - column = 0; - } - } -*/ } basic_region::~basic_region() { - for( auto section : m_sections ) { if( section != nullptr ) { delete section; } } + for( auto *section : m_sections ) { if( section != nullptr ) { delete section; } } } // legacy method, updates sounds and polls event launchers around camera @@ -500,6 +595,25 @@ basic_region::update() { } } +// legacy method, finds and assigns traction piece(s) to pantographs of provided vehicle +void +basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { + // TODO: convert vectors to transformation matrix and pass them down the chain along with calculated position + auto const vFront = glm::make_vec3( Vehicle->VectorFront().getArray() ); // wektor normalny dla płaszczyzny ruchu pantografu + auto const vUp = glm::make_vec3( Vehicle->VectorUp().getArray() ); // wektor pionu pudła (pochylony od pionu na przechyłce) + auto const vLeft = glm::make_vec3( Vehicle->VectorLeft().getArray() ); // wektor odległości w bok (odchylony od poziomu na przechyłce) + auto const position = glm::dvec3 { Vehicle->GetPosition() }; // współrzędne środka pojazdu + + auto p = Vehicle->pants[ Pantographindex ].fParamPants; + auto const pant0 = position + ( vLeft * p->vPos.z ) + ( vUp * p->vPos.y ) + ( vFront * p->vPos.x ); + p->PantTraction = std::numeric_limits::max(); // taka za duża wartość + + auto const §ionlist = sections( pant0, 0.0 ); + for( auto *section : sectionlist ) { + section->update_traction( Vehicle, Pantographindex ); + } +} + void basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { @@ -566,15 +680,15 @@ void basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Path->location(); + auto location = Path->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Path ); + section( location ).insert( Path ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } // also register path ends in appropriate sections, for path merging lookups // TODO: clean this up during track refactoring @@ -588,15 +702,15 @@ void basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) { // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Traction->location(); + auto location = Traction->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Traction ); + section( location ).insert( Traction ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } // also register traction ends in appropriate sections, for path merging lookups // TODO: clean this up during track refactoring @@ -610,15 +724,15 @@ void basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) { // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Instance->location(); + auto location = Instance->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Instance ); + section( location ).insert( Instance ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } } @@ -626,20 +740,34 @@ basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) void basic_region::insert_sound( TTextSound *Sound, scratch_data &Scratchpad ) { -/* - // TODO: implement // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Sound->location(); + auto location = Sound->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Instance ); + section( location ).insert( Sound ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: sound node \"" + Sound->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); + } +} + +// inserts provided event launcher in the region +void +basic_region::insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad ) { + + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + auto location = Launcher->location(); + + if( point_inside( location ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( location ).insert( Launcher ); + } + else { + // tracks are guaranteed to hava a name so we can skip the check + ErrorLog( "Bad scenario: event launcher \"" + Launcher->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } -*/ } // find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers @@ -757,7 +885,7 @@ void basic_region::register_path( TTrack *Path, glm::dvec3 const &Point ) { if( point_inside( Point ) ) { - section( Point ).register_end( Path, Point ); + section( Point ).register_node( Path, Point ); } } @@ -766,7 +894,7 @@ void basic_region::register_traction( TTraction *Traction, glm::dvec3 const &Point ) { if( point_inside( Point ) ) { - section( Point ).register_end( Traction, Point ); + section( Point ).register_node( Traction, Point ); } } diff --git a/scene.h b/scene.h index 1ccd42ba..05cfa1d3 100644 --- a/scene.h +++ b/scene.h @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include #include #include +#include #include "parser.h" #include "openglgeometrybank.h" @@ -24,7 +25,7 @@ namespace scene { int const EU07_CELLSIZE = 250; int const EU07_SECTIONSIZE = 1000; -int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of 1km sections along a side of square region +int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side of square region struct scratch_data { @@ -42,6 +43,8 @@ struct scratch_data { TDynamicObject * driver { nullptr }; bool is_open { false }; } trainset; + + bool initialized { false }; }; // basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision @@ -55,6 +58,9 @@ public: // legacy method, updates sounds and polls event launchers within radius around specified point void update(); + // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle + void + update_traction( TDynamicObject *Vehicle, int const Pantographindex ); // adds provided shape to the cell void insert( shape_node Shape ); @@ -67,6 +73,12 @@ public: // adds provided model instance to the cell void insert( TAnimModel *Instance ); + // adds provided sound instance to the cell + void + insert( TTextSound *Sound ); + // adds provided event launcher to the cell + void + insert( TEventLauncher *Launcher ); // registers provided path in the lookup directory of the cell void register_end( TTrack *Path ); @@ -100,10 +112,12 @@ private: // types using shapenode_sequence = std::vector; using path_sequence = std::vector; - using path_set = std::set; +// using path_set = std::unordered_set; using traction_sequence = std::vector; - using traction_set = std::set; +// using traction_set = std::unordered_set; using instance_sequence = std::vector; + using sound_sequence = std::vector; + using eventlauncher_sequence = std::vector; // members scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) }; bool m_active { false }; // whether the cell holds any actual data @@ -114,10 +128,12 @@ private: instance_sequence m_instancesopaque; instance_sequence m_instancetranslucent; traction_sequence m_traction; + sound_sequence m_sounds; + eventlauncher_sequence m_eventlaunchers; // search helpers struct lookup_data { - path_set paths; - traction_set traction; + path_sequence paths; + traction_sequence traction; } m_directories; }; @@ -131,24 +147,22 @@ public: // legacy method, updates sounds and polls event launchers within radius around specified point void update( glm::dvec3 const &Location, float const Radius ); + // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle + void + update_traction( TDynamicObject *Vehicle, int const Pantographindex ); // adds provided shape to the section void insert( shape_node Shape ); - // adds provided path to the section + // adds provided node to the section + template void - insert( TTrack *Path ); - // adds provided path to the section + insert( Type_ *Node ) { + cell( Node->location() ).insert( Node ); } + // registers provided node in the lookup directory of the section enclosing specified point + template void - insert( TTraction *Traction ); - // adds provided model instance to the section - void - insert( TAnimModel *Instance ); - // registers specified end point of the provided path in the lookup directory of the region - void - register_end( TTrack *Path, glm::dvec3 const &Point ); - // registers specified end point of the provided traction piece in the lookup directory of the region - void - register_end( TTraction *Traction, glm::dvec3 const &Point ); + register_node( Type_ *Node, glm::dvec3 const &Point ) { + cell( Point ).register_end( Node ); } // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); @@ -206,6 +220,9 @@ public: // legacy method, updates sounds and polls event launchers around camera void update(); + // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle + void + update_traction( TDynamicObject *Vehicle, int const Pantographindex ); // inserts provided shape in the region void insert_shape( shape_node Shape, scratch_data &Scratchpad ); @@ -221,6 +238,9 @@ public: // inserts provided sound in the region void insert_sound( TTextSound *Sound, scratch_data &Scratchpad ); + // inserts provided event launcher in the region + void + insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad ); // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); diff --git a/scenenode.h b/scenenode.h index ea4e029f..99e44f23 100644 --- a/scenenode.h +++ b/scenenode.h @@ -26,8 +26,8 @@ struct lighting_data { inline bool operator==( lighting_data const &Left, lighting_data const &Right ) { - return ( ( Left.diffuse == Right.diffuse ) - && ( Left.ambient == Right.ambient ) + return ( ( Left.diffuse == Right.diffuse ) + && ( Left.ambient == Right.ambient ) && ( Left.specular == Right.specular ) ); } inline diff --git a/simulation.cpp b/simulation.cpp index 843d23b1..0d0415d6 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -43,8 +43,8 @@ state_manager::deserialize( std::string const &Scenariofile ) { if( false == scenarioparser.ok() ) { return false; } deserialize( scenarioparser ); - // TODO: initialize links between loaded nodes + Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; } @@ -126,6 +126,11 @@ state_manager::deserialize( cParser &Input ) { token = Input.getToken(); } + + if( false == importscratchpad.initialized ) { + // manually perform scenario initialization + deserialize_firstinit( Input, importscratchpad ); + } } void @@ -247,11 +252,15 @@ state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpa void state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { - // TODO: implement + if( true == Scratchpad.initialized ) { return; } + simulation::Paths.InitTracks(); simulation::Traction.InitTraction(); simulation::Events.InitEvents(); + simulation::Events.InitLaunchers(); simulation::Memory.InitCells(); + + Scratchpad.initialized = true; } void @@ -283,7 +292,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad 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 ) + ")" ); + 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 @@ -303,7 +312,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_path( path, Scratchpad ); } else { - ErrorLog( "Bad scenario: track with duplicate name, \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); /* delete path; delete pathnode; @@ -320,7 +329,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_traction( traction, Scratchpad ); } else { - ErrorLog( "Bad scenario: traction piece with duplicate name, \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } else if( nodedata.type == "tractionpowersource" ) { @@ -336,7 +345,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad */ } else { - ErrorLog( "Bad scenario: power grid source with duplicate name, \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } else if( nodedata.type == "model" ) { @@ -355,7 +364,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_instance( instance, Scratchpad ); } else { - ErrorLog( "Bad scenario: 3d model instance with duplicate name, \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } } @@ -382,12 +391,25 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad */ } else { - ErrorLog( "Bad scenario: memory cell with duplicate name, \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: memory cell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } else if( nodedata.type == "eventlauncher" ) { - // TODO: implement - skip_until( Input, "end" ); + + auto *eventlauncher{ deserialize_eventlauncher( Input, Scratchpad, nodedata ) }; + if( simulation::Events.insert( eventlauncher ) ) { + // 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_launcher( eventlauncher, Scratchpad ); + } + } + else { + ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } } else if( nodedata.type == "sound" ) { @@ -396,7 +418,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_sound( sound, Scratchpad ); } else { - ErrorLog( "Bad scenario: sound node with duplicate name, \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } @@ -604,6 +626,23 @@ state_manager::deserialize_memorycell( cParser &Input, scene::scratch_data &Scra return memorycell; } +TEventLauncher * +state_manager::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_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { diff --git a/simulation.h b/simulation.h index 781785d4..14740ecd 100644 --- a/simulation.h +++ b/simulation.h @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "scene.h" #include "event.h" #include "memcell.h" +#include "evlaunch.h" #include "track.h" #include "traction.h" #include "tractionpower.h" @@ -60,6 +61,7 @@ private: 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 ); TTextSound * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );