From de4e10ab394ef20f5bd95d6c0a36727d773cde1c Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 11 Oct 2017 20:21:05 +0200 Subject: [PATCH] continued refacoring: vehicles, events, memcells, tracks, traction, traction power sources; minor readability enhancements and bug fixes --- AdvSound.cpp | 17 +- AdvSound.h | 19 +- AnimModel.cpp | 36 ++-- AnimModel.h | 4 +- Classes.h | 4 +- DynObj.cpp | 70 ++++++- DynObj.h | 187 ++++++++----------- Event.cpp | 248 ++++++++++++++----------- Event.h | 14 +- Gauge.h | 9 +- Globals.cpp | 2 + Globals.h | 2 + Ground.cpp | 189 +++++++------------ Ground.h | 8 +- MemCell.cpp | 12 ++ MemCell.h | 8 +- Model3d.cpp | 2 +- RealSound.cpp | 36 +--- RealSound.h | 75 ++++---- Segment.cpp | 39 ++-- Segment.h | 36 ---- Track.cpp | 63 +++++-- Track.h | 4 +- Traction.cpp | 322 ++++++++++++++++---------------- Traction.h | 5 +- TractionPower.cpp | 119 ++++++------ TractionPower.h | 45 +++-- Train.cpp | 12 +- Train.h | 1 - World.cpp | 201 +++++++++++++------- openglgeometrybank.h | 12 +- parser.cpp | 14 +- renderer.cpp | 36 +++- scene.cpp | 425 ++++++++++++++++++++++++++++++++++++++++++- scene.h | 115 +++++++++++- scenenode.cpp | 3 +- scenenode.h | 15 +- simulation.cpp | 326 ++++++++++++++++++++++++++++++--- simulation.h | 21 ++- 39 files changed, 1849 insertions(+), 907 deletions(-) diff --git a/AdvSound.cpp b/AdvSound.cpp index 7aefa52f..94870c95 100644 --- a/AdvSound.cpp +++ b/AdvSound.cpp @@ -19,12 +19,7 @@ TAdvancedSound::~TAdvancedSound() // SoundShut.Stop(); } -void TAdvancedSound::Free() -{ -} - -void TAdvancedSound::Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, - vector3 const &pPosition) +void TAdvancedSound::Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, Math3D::vector3 const &pPosition) { SoundStart.Init(NameOn, DistanceAttenuation, pPosition.x, pPosition.y, pPosition.z, true); SoundCommencing.Init(Name, DistanceAttenuation, pPosition.x, pPosition.y, pPosition.z, true); @@ -47,7 +42,7 @@ void TAdvancedSound::Init( std::string const &NameOn, std::string const &Name, s SoundShut.FA = 0.0; } -void TAdvancedSound::Load(cParser &Parser, vector3 const &pPosition) +void TAdvancedSound::Load(cParser &Parser, Math3D::vector3 const &pPosition) { std::string nameon, name, nameoff; double attenuation; @@ -62,7 +57,7 @@ void TAdvancedSound::Load(cParser &Parser, vector3 const &pPosition) Init( nameon, name, nameoff, attenuation, pPosition ); } -void TAdvancedSound::TurnOn(bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::TurnOn(bool ListenerInside, Math3D::vector3 NewPosition) { // hunter-311211: nie trzeba czekac na ponowne odtworzenie dzwieku, az sie wylaczy if ((State == ss_Off || State == ss_ShuttingDown) && (SoundStart.AM > 0)) @@ -76,7 +71,7 @@ void TAdvancedSound::TurnOn(bool ListenerInside, vector3 NewPosition) } } -void TAdvancedSound::TurnOff(bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::TurnOff(bool ListenerInside, Math3D::vector3 NewPosition) { if ((State == ss_Commencing || State == ss_Starting) && (SoundShut.AM > 0)) { @@ -90,7 +85,7 @@ void TAdvancedSound::TurnOff(bool ListenerInside, vector3 NewPosition) } } -void TAdvancedSound::Update(bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::Update(bool ListenerInside, Math3D::vector3 NewPosition) { if ((State == ss_Commencing) && (SoundCommencing.AM > 0)) { @@ -126,7 +121,7 @@ void TAdvancedSound::Update(bool ListenerInside, vector3 NewPosition) } } -void TAdvancedSound::UpdateAF(double A, double F, bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::UpdateAF(double A, double F, bool ListenerInside, Math3D::vector3 NewPosition) { // update, ale z amplituda i czestotliwoscia if( State == ss_Off ) { return; diff --git a/AdvSound.h b/AdvSound.h index 1049e750..f5c4de40 100644 --- a/AdvSound.h +++ b/AdvSound.h @@ -13,13 +13,13 @@ http://mozilla.org/MPL/2.0/. #include "RealSound.h" #include "parser.h" -typedef enum -{ +enum TSoundState { + ss_Off, ss_Starting, ss_Commencing, ss_ShuttingDown -} TSoundState; +}; class TAdvancedSound { // klasa dźwięków mających początek, dowolnie długi środek oraz zakończenie (np. Rp1) @@ -36,13 +36,12 @@ class TAdvancedSound public: TAdvancedSound() = default; ~TAdvancedSound(); - void Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, vector3 const &pPosition); - void Load(cParser &Parser, vector3 const &pPosition); - void TurnOn(bool ListenerInside, vector3 NewPosition); - void TurnOff(bool ListenerInside, vector3 NewPosition); - void Free(); - void Update(bool ListenerInside, vector3 NewPosition); - void UpdateAF(double A, double F, bool ListenerInside, vector3 NewPosition); + void Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, Math3D::vector3 const &pPosition); + void Load(cParser &Parser, Math3D::vector3 const &pPosition); + void TurnOn(bool ListenerInside, Math3D::vector3 NewPosition); + void TurnOff(bool ListenerInside, Math3D::vector3 NewPosition); + void Update(bool ListenerInside, Math3D::vector3 NewPosition); + void UpdateAF(double A, double F, bool ListenerInside, Math3D::vector3 NewPosition); void CopyIfEmpty(TAdvancedSound &s); }; diff --git a/AnimModel.cpp b/AnimModel.cpp index 5646811d..56eaa3b1 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -759,21 +759,27 @@ void TAnimModel::LightSet(int n, float v) { // ustawienie światła (n) na wartość (v) if (n >= iMaxNumLights) return; // przekroczony zakres - lsLights[n] = TLightState(int(v)); - switch (lsLights[n]) - { // interpretacja ułamka zależnie od typu - case 0: // ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz) - break; - case 1: // ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu - break; - case 2: // ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s) - break; - case 3: // zapalenie świateł zależne od oświetlenia scenerii - if (v > 3.0) - fDark = v - 3.0; // ustawienie indywidualnego progu zapalania - else - fDark = 0.25; // standardowy próg zaplania - break; + lsLights[ n ] = TLightState( static_cast( v ) ); + switch( lsLights[ n ] ) { + // interpretacja ułamka zależnie od typu + case ls_Off: { + // ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz) + break; + } + case ls_On: { + // ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu + break; + } + case ls_Blink: { + // ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s) + break; + } + case ls_Dark: { + // zapalenie świateł zależne od oświetlenia scenerii + if( v > 3.0 ) { fDark = v - 3.0; } // ustawienie indywidualnego progu zapalania + else { fDark = 0.25; } // standardowy próg zaplania + break; + } } }; //--------------------------------------------------------------------------- diff --git a/AnimModel.h b/AnimModel.h index 69bdc8bb..47d1c25a 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -176,7 +176,9 @@ private: unsigned int m_framestamp { 0 }; // id of last rendered gfx frame }; -class instance_manager : public basic_table { + + +class instance_table : public basic_table { }; diff --git a/Classes.h b/Classes.h index b8967103..74111f52 100644 --- a/Classes.h +++ b/Classes.h @@ -37,7 +37,7 @@ class TMtableTime; // czas dla danego posterunku class TController; // obiekt sterujący pociągiem (AI) -typedef enum +enum TCommandType { // binarne odpowiedniki komend w komórce pamięci cm_Unknown, // ciąg nierozpoznany (nie jest komendą) cm_Ready, // W4 zezwala na odjazd, ale semafor może zatrzymać @@ -51,6 +51,6 @@ typedef enum cm_OutsideStation, cm_Shunt, cm_Command // komenda pobierana z komórki -} TCommandType; +}; #endif diff --git a/DynObj.cpp b/DynObj.cpp index aaadd391..cbf34062 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4150,7 +4150,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, // Ra 15-01: gałka nastawy hamulca parser.getTokens(); parser >> asAnimName; - smBrakeMode = mdModel->GetFromName(asAnimName.c_str()); + smBrakeMode = mdModel->GetFromName(asAnimName); // jeszcze wczytać kąty obrotu dla poszczególnych ustawień } @@ -4158,7 +4158,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, // Ra 15-01: gałka nastawy hamulca parser.getTokens(); parser >> asAnimName; - smLoadMode = mdModel->GetFromName(asAnimName.c_str()); + smLoadMode = mdModel->GetFromName(asAnimName); // jeszcze wczytać kąty obrotu dla poszczególnych ustawień } @@ -4170,7 +4170,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for (i = 0; i < iAnimType[ANIM_WHEELS]; ++i) // liczba osi { // McZapkie-050402: wyszukiwanie kol o nazwie str* asAnimName = token + std::to_string(i + 1); - pAnimations[i].smAnimated = mdModel->GetFromName(asAnimName.c_str()); // ustalenie submodelu + pAnimations[i].smAnimated = mdModel->GetFromName(asAnimName); // ustalenie submodelu if (pAnimations[i].smAnimated) { //++iAnimatedAxles; pAnimations[i].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu @@ -4311,8 +4311,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, } } else - ErrorLog("Bad model: " + asFileName + " - missed submodel " + - asAnimName); // brak ramienia + ErrorLog("Bad model: " + asFileName + " - missed submodel " + asAnimName); // brak ramienia } } @@ -4346,10 +4345,9 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, } } else - ErrorLog( "Bad model: " + asFileName + " - missed submodel " + - asAnimName ); // brak ramienia + ErrorLog( "Bad model: " + asFileName + " - missed submodel " + asAnimName ); // brak ramienia } - } + } } else if( token == "animpantrg1prefix:" ) { @@ -5393,3 +5391,59 @@ TDynamicObject::ConnectedEnginePowerSource( TDynamicObject const *Caller ) const // ...if we're still here, report lack of power source return MoverParameters->EnginePowerSource.SourceType; } + + + +// legacy method, calculates changes in simulation state over specified time +void +vehicle_table::update( double Deltatime, int Iterationcount ) { + // Ra: w zasadzie to trzeba by utworzyć oddzielną listę taboru do liczenia fizyki + // na którą by się zapisywały wszystkie pojazdy będące w ruchu + // pojazdy stojące nie potrzebują aktualizacji, chyba że np. ktoś im zmieni nastawę hamulca + // oddzielną listę można by zrobić na pojazdy z napędem, najlepiej posortowaną wg typu napędu + 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 ); +*/ + } + vehicle->MoverParameters->ComputeConstans(); + vehicle->CoupleDist(); + } + if( Iterationcount > 1 ) { + // ABu: ponizsze wykonujemy tylko jesli wiecej niz jedna iteracja + for( int iteration = 0; iteration < ( Iterationcount - 1 ); ++iteration ) { + for( auto *vehicle : m_items ) { + vehicle->UpdateForce( Deltatime, Deltatime, false ); + } + for( auto *vehicle : m_items ) { + vehicle->FastUpdate( Deltatime ); + } + } + } + + auto const totaltime { Deltatime * Iterationcount }; // całkowity czas + + for( auto *vehicle : m_items ) { + vehicle->UpdateForce( Deltatime, totaltime, true ); + } + for( auto *vehicle : m_items ) { + // Ra 2015-01: tylko tu przelicza sieć trakcyjną + vehicle->Update( Deltatime, totaltime ); + } +/* + // TODO: re-implement + if (bDynamicRemove) + { // jeśli jest coś do usunięcia z listy, to trzeba na końcu + for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) + if ( false == Current->DynamicObject->bEnabled) + { + DynamicRemove(Current->DynamicObject); // usunięcie tego i podłączonych + Current = nRootDynamic; // sprawdzanie listy od początku + } + bDynamicRemove = false; // na razie koniec + } +*/ +} diff --git a/DynObj.h b/DynObj.h index 623b6ac4..df0794e9 100644 --- a/DynObj.h +++ b/DynObj.h @@ -160,19 +160,17 @@ private: // położenie pojazdu w świecie oraz parametry ruchu TTrackParam tp; // parametry toru przekazywane do fizyki TTrackFollower Axle0; // oś z przodu (od sprzęgu 0) TTrackFollower Axle1; // oś z tyłu (od sprzęgu 1) - int iAxleFirst; // numer pierwszej osi w kierunku ruchu (oś wiążąca pojazd z torem i wyzwalająca - // eventy) + int iAxleFirst; // numer pierwszej osi w kierunku ruchu (oś wiążąca pojazd z torem i wyzwalająca eventy) float fAxleDist; // rozstaw wózków albo osi do liczenia proporcji zacienienia Math3D::vector3 modelRot; // obrot pudła względem świata - do przeanalizowania, czy potrzebne!!! - // bool bCameraNear; //blisko kamer są potrzebne dodatkowe obliczenia szczegółów TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer, int &CouplNr ); -public: // parametry położenia pojazdu dostępne publicznie +public: + // parametry położenia pojazdu dostępne publicznie std::string asTrack; // nazwa toru początkowego; wywalić? std::string asDestination; // dokąd pojazd ma być kierowany "(stacja):(tor)" Math3D::matrix4x4 mMatrix; // macierz przekształcenia do renderowania modeli TMoverParameters *MoverParameters; // parametry fizyki ruchu oraz przeliczanie - // TMoverParameters *pControlled; //wskaźnik do sterowanego członu silnikowego TDynamicObject *NextConnected; // pojazd podłączony od strony sprzęgu 1 (kabina -1) TDynamicObject *PrevConnected; // pojazd podłączony od strony sprzęgu 0 (kabina 1) int NextConnectedNo; // numer sprzęgu podłączonego z tyłu @@ -182,7 +180,7 @@ public: // parametry położenia pojazdu dostępne publicznie TPowerSource ConnectedEnginePowerSource( TDynamicObject const *Caller ) const; -public: // modele składowe pojazdu + // modele składowe pojazdu TModel3d *mdModel; // model pudła TModel3d *mdLoad; // model zmiennego ładunku TModel3d *mdKabina; // model kabiny dla użytkownika; McZapkie-030303: to z train.h @@ -201,28 +199,26 @@ public: // modele składowe pojazdu bool SectionLightsActive { false }; // flag indicating whether section lights were set. float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) - private: // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe +private: + // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe material_data m_materialdata; - public: +public: inline material_data const *Material() const { return &m_materialdata; } // tymczasowo udostępnione do wyszukiwania drutu int iAnimType[ ANIM_TYPES ]; // 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki - private: +private: int iAnimations; // liczba obiektów animujących /* TAnim *pAnimations; // obiekty animujące (zawierają wskaźnik do funkcji wykonującej animację) */ std::vector pAnimations; - TSubModel ** - pAnimated; // lista animowanych submodeli (może być ich więcej niż obiektów animujących) - double dWheelAngle[3]; // kąty obrotu kół: 0=przednie toczne, 1=napędzające i wiązary, 2=tylne - // toczne - void - UpdateNone(TAnim *pAnim){}; // animacja pusta (funkcje ustawiania submodeli, gdy blisko kamery) + TSubModel ** pAnimated; // lista animowanych submodeli (może być ich więcej niż obiektów animujących) + double dWheelAngle[3]; // kąty obrotu kół: 0=przednie toczne, 1=napędzające i wiązary, 2=tylne toczne + void UpdateNone(TAnim *pAnim){}; // animacja pusta (funkcje ustawiania submodeli, gdy blisko kamery) void UpdateAxle(TAnim *pAnim); // animacja osi void UpdateBoogie(TAnim *pAnim); // animacja wózka void UpdateDoorTranslate(TAnim *pAnim); // animacja drzwi - przesuw @@ -268,8 +264,7 @@ public: // modele składowe pojazdu TButton btCoupler1; // sprzegi TButton btCoupler2; - TAirCoupler - btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki + TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki TAirCoupler btCPneumatic2; TAirCoupler btCPneumatic1r; // ABu: to zeby nie bylo problemow przy laczeniu wagonow, TAirCoupler btCPneumatic2r; // jesli beda polaczone sprzegami 1<->1 lub 0<->0 @@ -300,8 +295,6 @@ public: // modele składowe pojazdu TButton btHeadSignals23; TButton btMechanik1; TButton btMechanik2; - //TSubModel *smMechanik0; // Ra: mechanik wbudowany w model jako submodel? - //TSubModel *smMechanik1; // mechanik od strony sprzęgu 1 double enginevolume; // MC: pomocnicze zeby gladziej silnik buczal int iAxles; // McZapkie: to potem mozna skasowac i zastapic iNumAxles @@ -325,7 +318,6 @@ public: // modele składowe pojazdu TAdvancedSound sReleaser; // Winger 010304 - // TRealSound rsPanTup; //PSound sPantUp; TRealSound sPantUp; TRealSound sPantDown; TRealSound rsDoorOpen; // Ra: przeniesione z kabiny @@ -352,8 +344,6 @@ public: // modele składowe pojazdu int iHornWarning; // numer syreny do użycia po otrzymaniu sygnału do jazdy bool bEnabled; // Ra: wyjechał na portal i ma być usunięty protected: - // TTrackFollower Axle2; //dwie osie z czterech (te są protected) - // TTrackFollower Axle3; //Ra: wyłączyłem, bo kąty są liczone w Segment.cpp int iNumAxles; // ilość osi std::string asModel; @@ -373,26 +363,18 @@ public: // modele składowe pojazdu TDynamicObject * PrevC(int C); TDynamicObject * NextC(int C); double NextDistance(double d = -1.0); - void SetdMoveLen(double dMoveLen) - { - MoverParameters->dMoveLen = dMoveLen; - } - void ResetdMoveLen() - { - MoverParameters->dMoveLen = 0; - } - double GetdMoveLen() - { - return MoverParameters->dMoveLen; - } + void SetdMoveLen(double dMoveLen) { + MoverParameters->dMoveLen = dMoveLen; } + void ResetdMoveLen() { + MoverParameters->dMoveLen = 0; } + double GetdMoveLen() { + return MoverParameters->dMoveLen; } int GetPneumatic(bool front, bool red); void SetPneumatic(bool front, bool red); std::string asName; - std::string GetName() - { - return this ? asName : std::string(""); - }; + std::string name() const { + return this ? asName : std::string(); }; TRealSound rsDiesielInc; // youBy TRealSound rscurve; // youBy @@ -405,7 +387,6 @@ public: // modele składowe pojazdu TDynamicObject * ABuScanNearestObject(TTrack *Track, double ScanDir, double ScanDist, int &CouplNr); TDynamicObject * GetFirstDynamic(int cpl_type, int cf = 1); - // TDynamicObject* GetFirstCabDynamic(int cpl_type); void ABuSetModelShake( Math3D::vector3 mShake); // McZapkie-010302 @@ -437,99 +418,65 @@ public: // modele składowe pojazdu void Move(double fDistance); void FastMove(double fDistance); void RenderSounds(); - inline Math3D::vector3 GetPosition() const - { - return vPosition; - }; - inline Math3D::vector3 HeadPosition() - { - return vCoulpler[iDirection ^ 1]; - }; // pobranie współrzędnych czoła - inline Math3D::vector3 RearPosition() - { - return vCoulpler[iDirection]; - }; // pobranie współrzędnych tyłu - inline Math3D::vector3 AxlePositionGet() - { - return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; - }; - inline Math3D::vector3 VectorFront() const - { - return vFront; - }; - inline Math3D::vector3 VectorUp() - { - return vUp; - }; - inline Math3D::vector3 VectorLeft() const - { - return vLeft; - }; - inline double * Matrix() - { - return mMatrix.getArray(); - }; - inline double GetVelocity() - { - return MoverParameters->Vel; - }; - inline double GetLength() const - { - return MoverParameters->Dim.L; - }; - inline double GetWidth() const - { - return MoverParameters->Dim.W; - }; - inline TTrack * GetTrack() - { - return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack()); - }; - // void UpdatePos(); + inline Math3D::vector3 GetPosition() const { + return vPosition; }; + // pobranie współrzędnych czoła + inline Math3D::vector3 HeadPosition() { + return vCoulpler[iDirection ^ 1]; }; + // pobranie współrzędnych tyłu + inline Math3D::vector3 RearPosition() { + return vCoulpler[iDirection]; }; + inline Math3D::vector3 AxlePositionGet() { + return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; }; + inline Math3D::vector3 VectorFront() const { + return vFront; }; + inline Math3D::vector3 VectorUp() { + return vUp; }; + inline Math3D::vector3 VectorLeft() const { + return vLeft; }; + inline double * Matrix() { + return mMatrix.getArray(); }; + inline double GetVelocity() { + return MoverParameters->Vel; }; + inline double GetLength() const { + return MoverParameters->Dim.L; }; + inline double GetWidth() const { + return MoverParameters->Dim.W; }; + inline TTrack * GetTrack() { + return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack()); }; // McZapkie-260202 void LoadMMediaFile(std::string BaseDir, std::string TypeName, std::string ReplacableSkin); - inline double ABuGetDirection() const // ABu. - { - return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); - }; - // inline double ABuGetTranslation() //ABu. - // {//zwraca przesunięcie wózka względem Point1 toru - // return (Axle1.GetTrack()==MyTrack?Axle1.GetTranslation():Axle0.GetTranslation()); - // }; - inline double RaDirectionGet() - { // zwraca kierunek pojazdu na torze z aktywną osą - return iAxleFirst ? Axle1.GetDirection() : Axle0.GetDirection(); - }; - inline double RaTranslationGet() - { // zwraca przesunięcie wózka względem Point1 toru z aktywną osią - return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation(); - }; - inline TTrack * RaTrackGet() - { // zwraca tor z aktywną osią - return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack(); - }; + inline double ABuGetDirection() const { // ABu. + return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); }; + // zwraca kierunek pojazdu na torze z aktywną osą + inline double RaDirectionGet() { + return iAxleFirst ? Axle1.GetDirection() : Axle0.GetDirection(); }; + // zwraca przesunięcie wózka względem Point1 toru z aktywną osią + inline double RaTranslationGet() { + return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation(); }; + // zwraca tor z aktywną osią + inline TTrack * RaTrackGet() { + return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack(); }; void CouplersDettach(double MinDist, int MyScanDir); void RadioStop(); void Damage(char flag); void RaLightsSet(int head, int rear); - // void RaAxleEvent(TEvent *e); TDynamicObject * FirstFind(int &coupler_nr, int cf = 1); float GetEPP(); // wyliczanie sredniego cisnienia w PG int DirectionSet(int d); // ustawienie kierunku w składzie - int DirectionGet() - { - return iDirection + iDirection - 1; - }; // odczyt kierunku w składzie + // odczyt kierunku w składzie + int DirectionGet() { + return iDirection + iDirection - 1; }; int DettachStatus(int dir); int Dettach(int dir); TDynamicObject * Neightbour(int &dir); void CoupleDist(); TDynamicObject * ControlledFind(); void ParamSet(int what, int into); - int RouteWish(TTrack *tr); // zapytanie do AI, po którym segmencie skrzyżowania - // jechać + // zapytanie do AI, po którym segmencie skrzyżowania jechać + int RouteWish(TTrack *tr); void DestinationSet(std::string to, std::string numer); std::string TextureTest(std::string const &name); void OverheadTrack(float o); @@ -537,4 +484,14 @@ public: // modele składowe pojazdu static std::string const MED_labels[ 8 ]; }; + + +class vehicle_table : public basic_table { + +public: + // legacy method, calculates changes in simulation state over specified time + void + update( double dt, int iter ); +}; + //--------------------------------------------------------------------------- diff --git a/Event.cpp b/Event.cpp index 6d013b8a..389b92ca 100644 --- a/Event.cpp +++ b/Event.cpp @@ -74,10 +74,6 @@ TEvent::~TEvent() { }; -void TEvent::Init(){ - -}; - void TEvent::Conditions(cParser *parser, std::string s) { // przetwarzanie warunków, wspólne dla Multiple i UpdateValues if (s == "condition") @@ -949,7 +945,7 @@ event_manager::CheckQuery() { // TODO: re-enable when messaging module is in place if( Global::iMultiplayer ) { // potwierdzenie wykonania dla serwera (odczyt semafora już tak nie działa) - WyslijEvent( tmpEvent->asName, tmpEvent->Activator->GetName() ); + WyslijEvent( tmpEvent->asName, tmpEvent->Activator->name() ); } */ m_workevent->Params[ 9 ].asMemCell->PutCommand( @@ -984,61 +980,105 @@ event_manager::CheckQuery() { } break; } - case tp_Lights: - if (m_workevent->Params[9].asModel) - for (i = 0; i < iMaxNumLights; i++) - if (m_workevent->Params[i].asdouble >= 0) //-1 zostawia bez zmiany - m_workevent->Params[9].asModel->LightSet( - i, m_workevent->Params[i].asdouble); // teraz też ułamek - break; - case tp_Visible: - if (m_workevent->Params[9].nGroundNode) - m_workevent->Params[9].nGroundNode->bVisible = (m_workevent->Params[i].asInt > 0); - break; - case tp_Velocity: - Error("Not implemented yet :("); - break; - case tp_Exit: - MessageBox(0, m_workevent->asNodeName.c_str(), " THE END ", MB_OK); - Global::iTextMode = -1; // wyłączenie takie samo jak sekwencja F10 -> Y - return false; - case tp_Sound: - switch (m_workevent->Params[0].asInt) - { // trzy możliwe przypadki: - case 0: - m_workevent->Params[9].tsTextSound->Stop(); - break; - case 1: - m_workevent->Params[9].tsTextSound->Play( - 1, 0, true, m_workevent->Params[9].tsTextSound->vSoundPosition); - break; - case -1: - m_workevent->Params[9].tsTextSound->Play( - 1, DSBPLAY_LOOPING, true, m_workevent->Params[9].tsTextSound->vSoundPosition); - break; + case tp_Lights: { + if( m_workevent->Params[ 9 ].asModel ) { + for( i = 0; i < iMaxNumLights; ++i ) { + if( m_workevent->Params[ i ].asdouble >= 0 ) { + // -1 zostawia bez zmiany + m_workevent->Params[ 9 ].asModel->LightSet( + i, + m_workevent->Params[ i ].asdouble ); + } + } } break; + } + case tp_Visible: { +#ifdef EU07_USE_OLD_GROUNDCODE + if( m_workevent->Params[ 9 ].nGroundNode ) + m_workevent->Params[ 9 ].nGroundNode->bVisible = ( m_workevent->Params[ i ].asInt > 0 ); +#else + if( m_workevent->Params[ 9 ].asEditorNode ) + m_workevent->Params[ 9 ].asEditorNode->visible( m_workevent->Params[ i ].asInt > 0 ); +#endif + break; + } + case tp_Velocity: { + Error( "Not implemented yet :(" ); + break; + } + case tp_Exit: { + MessageBox( 0, m_workevent->asNodeName.c_str(), " THE END ", MB_OK ); + Global::iTextMode = -1; // wyłączenie takie samo jak sekwencja F10 -> Y + return false; + } + case tp_Sound: { + switch( m_workevent->Params[ 0 ].asInt ) { + // trzy możliwe przypadki: + case 0: { + m_workevent->Params[ 9 ].tsTextSound->Stop(); + break; + } + case 1: { + m_workevent->Params[ 9 ].tsTextSound->Play( + 1, + 0, + true, + m_workevent->Params[ 9 ].tsTextSound->vSoundPosition ); + break; + } + case -1: { + m_workevent->Params[ 9 ].tsTextSound->Play( + 1, + DSBPLAY_LOOPING, + true, + m_workevent->Params[ 9 ].tsTextSound->vSoundPosition ); + break; + } + default: { + break; + } + } + break; + } case tp_Disable: Error("Not implemented yet :("); break; - case tp_Animation: // Marcin: dorobic translacje - Ra: dorobiłem ;-) - if (m_workevent->Params[0].asInt == 1) - m_workevent->Params[9].asAnimContainer->SetRotateAnim( - Math3D::vector3(m_workevent->Params[1].asdouble, m_workevent->Params[2].asdouble, - m_workevent->Params[3].asdouble), - m_workevent->Params[4].asdouble); - else if (m_workevent->Params[0].asInt == 2) - m_workevent->Params[9].asAnimContainer->SetTranslateAnim( - Math3D::vector3(m_workevent->Params[1].asdouble, m_workevent->Params[2].asdouble, - m_workevent->Params[3].asdouble), - m_workevent->Params[4].asdouble); - else if (m_workevent->Params[0].asInt == 4) - m_workevent->Params[9].asModel->AnimationVND( - m_workevent->Params[8].asPointer, - m_workevent->Params[1].asdouble, // tu mogą być dodatkowe parametry, np. od-do - m_workevent->Params[2].asdouble, m_workevent->Params[3].asdouble, - m_workevent->Params[4].asdouble); + case tp_Animation: { + switch( m_workevent->Params[ 0 ].asInt ) { + case 1: { + m_workevent->Params[ 9 ].asAnimContainer->SetRotateAnim( + Math3D::vector3 { + m_workevent->Params[ 1 ].asdouble, + m_workevent->Params[ 2 ].asdouble, + m_workevent->Params[ 3 ].asdouble }, + m_workevent->Params[ 4 ].asdouble ); + break; + } + case 2: { + m_workevent->Params[ 9 ].asAnimContainer->SetTranslateAnim( + Math3D::vector3 { + m_workevent->Params[ 1 ].asdouble, + m_workevent->Params[ 2 ].asdouble, + m_workevent->Params[ 3 ].asdouble }, + m_workevent->Params[ 4 ].asdouble ); + break; + } + case 4: { + m_workevent->Params[ 9 ].asModel->AnimationVND( + m_workevent->Params[ 8 ].asPointer, + m_workevent->Params[ 1 ].asdouble, // tu mogą być dodatkowe parametry, np. od-do + m_workevent->Params[ 2 ].asdouble, + m_workevent->Params[ 3 ].asdouble, + m_workevent->Params[ 4 ].asdouble ); + break; + } + default: { + break; + } + } break; + } case tp_Switch: { if( m_workevent->Params[ 9 ].asTrack ) { m_workevent->Params[ 9 ].asTrack->Switch( @@ -1057,13 +1097,15 @@ event_manager::CheckQuery() { break; } case tp_TrackVel: - if (m_workevent->Params[9].asTrack) - { // prędkość na zwrotnicy może być ograniczona z góry we wpisie, większej się nie - // ustawi eventem - WriteLog("Type: TrackVel"); + if (m_workevent->Params[9].asTrack) { + // prędkość na zwrotnicy może być ograniczona z góry we wpisie, większej się nie ustawi eventem m_workevent->Params[9].asTrack->VelocitySet(m_workevent->Params[0].asdouble); - if (DebugModeFlag) // wyświetlana jest ta faktycznie ustawiona - WriteLog(" - velocity: ", m_workevent->Params[9].asTrack->VelocityGet()); + // wyświetlana jest ta faktycznie ustawiona + WriteLog( "Type: TrackVel - [" + + to_string( m_workevent->Params[ 0 ].asdouble, 2 ) + "]" + + ( DebugModeFlag ? + ", actual [ " + to_string( m_workevent->Params[ 9 ].asTrack->VelocityGet(), 2 ) + "]" : + "" ) ); } break; case tp_DynVel: @@ -1097,7 +1139,7 @@ event_manager::CheckQuery() { if( ( m_workevent->iFlags & conditional_anyelse ) == 0 ) { // jednoznaczne tylko, gdy nie było else if( m_workevent->Activator ) { - WyslijEvent( m_workevent->asName, m_workevent->Activator->GetName() ); + WyslijEvent( m_workevent->asName, m_workevent->Activator->name() ); } else { WyslijEvent( m_workevent->asName, "" ); @@ -1175,8 +1217,9 @@ event_manager::CheckQuery() { m_workevent->Params[ 9 ].asMemCell->UpdateValues( m_workevent->Activator->Mechanik->TrainName(), m_workevent->Activator->Mechanik->StationCount() - m_workevent->Activator->Mechanik->StationIndex(), // ile przystanków do końca - m_workevent->Activator->Mechanik->IsStop() ? 1 : - 0, // 1, gdy ma tu zatrzymanie + m_workevent->Activator->Mechanik->IsStop() ? + 1 : + 0, // 1, gdy ma tu zatrzymanie m_workevent->iFlags); WriteLog("Train detected: " + m_workevent->Activator->Mechanik->TrainName()); } @@ -1186,10 +1229,10 @@ event_manager::CheckQuery() { // zapisanie zawartości komórki pamięci do logu if( m_workevent->Params[ 9 ].asMemCell ) { // jeśli była podana nazwa komórki - WriteLog( "Memcell \"" + m_workevent->asNodeName + "\": " - + m_workevent->Params[ 9 ].asMemCell->Text() + " " - + std::to_string( m_workevent->Params[ 9 ].asMemCell->Value1() ) + " " - + std::to_string( m_workevent->Params[ 9 ].asMemCell->Value2() ) ); + WriteLog( "Memcell \"" + m_workevent->asNodeName + "\": [" + + m_workevent->Params[ 9 ].asMemCell->Text() + "] [" + + to_string( m_workevent->Params[ 9 ].asMemCell->Value1(), 2 ) + "] [" + + to_string( m_workevent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" ); } else { // TODO: re-enable when cell manager is in place @@ -1237,7 +1280,7 @@ event_manager::InitEvents() { 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 - if( cell ) { // McZapkie-100302 + if( cell != nullptr ) { // McZapkie-100302 if( Current->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 @@ -1281,7 +1324,7 @@ event_manager::InitEvents() { case tp_GetValues: case tp_WhoIs: { auto *cell = simulation::Memory.find( Current->asNodeName ); - if( cell ) { + if( cell != nullptr ) { Current->Params[ 8 ].asLocation = &( cell->location() ); Current->Params[ 9 ].asMemCell = cell; if( ( Current->Type == tp_GetValues ) @@ -1301,7 +1344,7 @@ event_manager::InitEvents() { case tp_CopyValues: { // skopiowanie komórki do innej auto *cell = simulation::Memory.find( Current->asNodeName ); // komórka docelowa - if( cell ) { + if( cell != nullptr ) { Current->Params[ 4 ].asLocation = &( cell->location() ); Current->Params[ 5 ].asMemCell = cell; // komórka docelowa if( false == cell->asTrackName.empty() ) { @@ -1338,7 +1381,7 @@ event_manager::InitEvents() { SafeDeleteArray( Current->Params[ 9 ].asText ); // egzemplarz modelu do animowania auto *instance = simulation::Instances.find( Current->asNodeName ); - if( instance ) { + if( instance != nullptr ) { if( Current->Params[ 0 ].asInt == 4 ) { // model dla całomodelowych animacji Current->Params[ 9 ].asModel = instance; @@ -1365,33 +1408,35 @@ event_manager::InitEvents() { case tp_Lights: { // zmiana świeteł modelu auto *instance = simulation::Instances.find( Current->asNodeName ); - if( instance ) + if( instance != nullptr ) Current->Params[ 9 ].asModel = instance; else ErrorLog( "Bad event: lights event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); Current->asNodeName = ""; break; } -/* case tp_Visible: { // ukrycie albo przywrócenie obiektu - tmp = FindGroundNode( Current->asNodeName, TP_MODEL ); // najpierw model - if( !tmp ) - tmp = FindGroundNode( Current->asNodeName, TP_TRACK ); // albo tory? - if( !tmp ) - tmp = FindGroundNode( Current->asNodeName, TP_TRACTION ); // może druty? - if( tmp ) - Current->Params[ 9 ].nGroundNode = tmp; + editor::basic_node *node = simulation::Instances.find( Current->asNodeName ); // najpierw model + if( node == nullptr ) { + // albo tory? + node = simulation::Paths.find( Current->asNodeName ); + } + if( node == nullptr ) { + // może druty? + node = simulation::Traction.find( Current->asNodeName ); + } + if( node != nullptr ) + Current->Params[ 9 ].asEditorNode = node; else ErrorLog( "Bad event: visibility event \"" + Current->asName + "\" cannot find item \"" + Current->asNodeName + "\"" ); Current->asNodeName = ""; break; } -*/ case tp_Switch: { // przełożenie zwrotnicy albo zmiana stanu obrotnicy auto *track = simulation::Paths.find( Current->asNodeName ); - if( track ) { + if( track != nullptr ) { // dowiązanie toru if( track->iAction == NULL ) { // jeśli nie jest zwrotnicą ani obrotnicą to będzie się zmieniał stan uszkodzenia @@ -1414,23 +1459,21 @@ event_manager::InitEvents() { Current->asNodeName = ""; break; } -/* case tp_Sound: { // odtworzenie dźwięku - tmp = FindGroundNode( Current->asNodeName, TP_SOUND ); - if( tmp ) - Current->Params[ 9 ].tsTextSound = tmp->tsStaticSound; + auto *sound = simulation::Sounds.find( Current->asNodeName ); + if( sound != nullptr ) + Current->Params[ 9 ].tsTextSound = sound; else ErrorLog( "Bad event: sound event \"" + Current->asName + "\" cannot find static sound \"" + Current->asNodeName + "\"" ); Current->asNodeName = ""; break; } -*/ case tp_TrackVel: { // ustawienie prędkości na torze if( false == Current->asNodeName.empty() ) { auto *track = simulation::Paths.find( Current->asNodeName ); - if( track ) { + if( track != nullptr ) { // flaga zmiany prędkości toru jest istotna dla skanowania track->iAction |= 0x200; Current->Params[ 9 ].asTrack = track; @@ -1442,22 +1485,20 @@ event_manager::InitEvents() { Current->asNodeName = ""; break; } -/* case tp_DynVel: { // komunikacja z pojazdem o konkretnej nazwie if( Current->asNodeName == "activator" ) Current->Params[ 9 ].asDynamic = nullptr; else { - tmp = FindGroundNode( Current->asNodeName, TP_DYNAMIC ); - if( tmp ) - Current->Params[ 9 ].asDynamic = tmp->DynamicObject; + auto *vehicle = simulation::Vehicles.find( Current->asNodeName ); + if( vehicle != nullptr ) + Current->Params[ 9 ].asDynamic = vehicle; else Error( "Bad event: vehicle velocity event \"" + Current->asName + "\" cannot find vehicle \"" + Current->asNodeName + "\"" ); } Current->asNodeName = ""; break; } -*/ case tp_Multiple: { std::string cellastext; if( Current->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora @@ -1494,36 +1535,27 @@ event_manager::InitEvents() { } break; } -/* case tp_Voltage: { // zmiana napięcia w zasilaczu (TractionPowerSource) - if( !Current->asNodeName.empty() ) { - tmp = FindGroundNode( Current->asNodeName, TP_TRACTIONPOWERSOURCE ); // podłączenie zasilacza - if( tmp ) - Current->Params[ 9 ].psPower = tmp->psTractionPowerSource; + if( false == Current->asNodeName.empty() ) { + auto *powersource = simulation::Powergrid.find( Current->asNodeName ); // podłączenie zasilacza + if( powersource != nullptr ) + Current->Params[ 9 ].psPower = powersource; else ErrorLog( "Bad event: voltage event \"" + Current->asName + "\" cannot find power source \"" + Current->asNodeName + "\"" ); } Current->asNodeName = ""; break; } -*/ case tp_Message: { // wyświetlenie komunikatu break; } } // switch - if( Current->fDelay < 0 ) { - AddToQuery( Current, nullptr ); - } + + if( Current->fDelay < 0 ) { AddToQuery( Current, nullptr ); } } -/* - for (TGroundNode *Current = nRootOfType[TP_MEMCELL]; Current; Current = Current->nNext) - { // Ra: eventy komórek pamięci, wykonywane po wysłaniu komendy do zatrzymanego pojazdu - Current->MemCell->AssignEvents( FindEvent( Current->asName + ":sent" ) ); - } -*/ } // legacy method, verifies condition for specified event diff --git a/Event.h b/Event.h index 340aa245..cd0f89cd 100644 --- a/Event.h +++ b/Event.h @@ -12,6 +12,9 @@ http://mozilla.org/MPL/2.0/. #include #include "dumb3d.h" #include "classes.h" +#include "names.h" +#include "scenenode.h" +#include "evlaunch.h" enum TEventType { tp_Unknown, @@ -61,6 +64,7 @@ union TParam void *asPointer; TMemCell *asMemCell; TGroundNode *nGroundNode; + editor::basic_node *asEditorNode; glm::dvec3 const *asLocation; TTrack *asTrack; TAnimModel *asModel; @@ -104,7 +108,6 @@ public: // metody TEvent(std::string const &m = ""); ~TEvent(); - void Init(); void Load(cParser *parser, Math3D::vector3 const &org); static void AddToQuery( TEvent *Event, TEvent *&Start ); std::string CommandGet(); @@ -123,9 +126,12 @@ public: ~event_manager(); // methods // adds provided event to the collection. returns: true on success - // TODO: return handle instead of pointer + // TBD, TODO: return handle to the event bool insert( TEvent *Event ); + bool + insert( TEventLauncher *Launcher ) { + return m_launchers.insert( Launcher ); } // legacy method, returns pointer to specified event, or null TEvent * FindEvent( std::string const &Name ); @@ -143,6 +149,7 @@ private: // types using event_sequence = std::deque; using event_map = std::unordered_map; + using eventlauncher_sequence = std::vector; // methods // legacy method, verifies condition for specified event @@ -158,8 +165,9 @@ private: // legacy version of the above TEvent *QueryRootEvent { nullptr }; TEvent *m_workevent { nullptr }; - event_map m_eventmap; + basic_table m_launchers; + eventlauncher_sequence m_launcherqueue; }; //--------------------------------------------------------------------------- diff --git a/Gauge.h b/Gauge.h index 516294a7..c437883c 100644 --- a/Gauge.h +++ b/Gauge.h @@ -12,15 +12,14 @@ http://mozilla.org/MPL/2.0/. #include "Classes.h" #include "sound.h" -typedef enum -{ // typ ruchu +enum TGaugeType { + // typ ruchu gt_Unknown, // na razie nie znany gt_Rotate, // obrót gt_Move, // przesunięcie równoległe - gt_Wiper, // obrót trzech kolejnych submodeli o ten sam kąt (np. wycieraczka, drzwi - // harmonijkowe) + gt_Wiper, // obrót trzech kolejnych submodeli o ten sam kąt (np. wycieraczka, drzwi harmonijkowe) gt_Digital // licznik cyfrowy, np. kilometrów -} TGaugeType; +}; // animowany wskaźnik, mogący przyjmować wiele stanów pośrednich class TGauge { diff --git a/Globals.cpp b/Globals.cpp index fea1cb39..f0e85b79 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -996,10 +996,12 @@ void Global::TrainDelete(TDynamicObject *d) pWorld->TrainDelete(d); }; +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *Global::DynamicNearest() { // ustalenie pojazdu najbliższego kamerze return pGround->DynamicNearest(pCamera->Pos); }; +#endif TDynamicObject *Global::CouplerNearest() { // ustalenie pojazdu najbliższego kamerze diff --git a/Globals.h b/Globals.h index 77abf302..e3e15a8d 100644 --- a/Globals.h +++ b/Globals.h @@ -297,7 +297,9 @@ class Global static void TrainDelete(TDynamicObject *d); static void ConfigParse(cParser &parser); static std::string GetNextSymbol(); +#ifdef EU07_USE_OLD_GROUNDCODE static TDynamicObject * DynamicNearest(); +#endif static TDynamicObject * CouplerNearest(); static bool AddToQuery(TEvent *event, TDynamicObject *who); static bool DoEvents(); diff --git a/Ground.cpp b/Ground.cpp index 8ffe1c38..ac467df8 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -586,7 +586,7 @@ void TGround::Free() // RootNode=NULL; nRootDynamic = NULL; } - +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode * TGround::DynamicFindAny(std::string const &Name) { // wyszukanie pojazdu o podanej nazwie, szukanie po wszystkich (użyć drzewa!) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) @@ -603,7 +603,7 @@ TGroundNode * TGround::DynamicFind(std::string const &Name) return Current; return NULL; }; - +#endif void TGround::DynamicList(bool all) { // odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się @@ -694,6 +694,7 @@ int iTrainSetWehicleNumber = 0; TGroundNode *nTrainSetNode = NULL; // poprzedni pojazd do łączenia TGroundNode *nTrainSetDriver = NULL; // pojazd, któremu zostanie wysłany rozkład +#ifdef EU07_USE_OLD_GROUNDCODE void TGround::RaTriangleDivider(TGroundNode *node) { // tworzy dodatkowe trójkąty i zmiejsza podany // to jest wywoływane przy wczytywaniu trójkątów @@ -814,7 +815,7 @@ void TGround::RaTriangleDivider(TGroundNode *node) RaTriangleDivider(node); // rekurencja, bo nawet na TD raz nie wystarczy RaTriangleDivider(ntri); }; - +#endif TGroundNode * TGround::AddGroundNode(cParser *parser) { // wczytanie wpisu typu "node" std::string str, str1, str2, str3, str4, Skin, DriverType, asNodeName; @@ -889,7 +890,8 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) switch (tmp->iType) { - case TP_TRACTION: +#ifdef EU07_USE_OLD_GROUNDCODE + case TP_TRACTION: tmp->hvTraction = new TTraction( tmp->asName ); parser->getTokens(); *parser >> token; @@ -983,7 +985,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) >> tmp->pCenter.y >> tmp->pCenter.z; tmp->pCenter += pOrigin; - tmp->psTractionPowerSource = new TTractionPowerSource(tmp); + tmp->psTractionPowerSource = new TTractionPowerSource( tmp->asName ); tmp->psTractionPowerSource->Load(parser); break; case TP_MEMCELL: @@ -1004,6 +1006,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } break; +#endif case TP_EVLAUNCH: parser->getTokens(3); *parser @@ -1015,6 +1018,7 @@ 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) @@ -1191,57 +1195,8 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } break; +#endif case TP_MODEL: { -#ifdef EU07_USE_OLD_TERRAINCODE - if( rmin < 0 ) { - tmp->iType = TP_TERRAIN; - tmp->fSquareMinRadius = 0; // to w ogóle potrzebne? - } - parser->getTokens( 3 ); - *parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z; - parser->getTokens(); - *parser >> tf1; - // OlO_EU&KAKISH-030103: obracanie punktow zaczepien w modelu - tmp->pCenter.RotateY( aRotate.y / 180.0 * M_PI ); - // McZapkie-260402: model tez ma wspolrzedne wzgledne - tmp->pCenter += pOrigin; - - tmp->Model = new TAnimModel(); - tmp->Model->RaAnglesSet( aRotate.x, tf1 + aRotate.y, aRotate.z ); // dostosowanie do pochylania linii - if( tmp->Model->Load( parser, tmp->iType == TP_TERRAIN ) ) { - // wczytanie modelu, tekstury i stanu świateł... - tmp->iFlags = tmp->Model->Flags() | 0x200; // ustalenie, czy przezroczysty; flaga usuwania - } - else if( tmp->iType != TP_TERRAIN ) { // model nie wczytał się - ignorowanie node - delete tmp; - tmp = NULL; // nie może być tu return - break; // nie może być tu return? - } - if( tmp->iType == TP_TERRAIN ) { // jeśli model jest terenem, trzeba utworzyć dodatkowe obiekty - // po wczytaniu model ma już utworzone DL albo VBO - Global::pTerrainCompact = tmp->Model; // istnieje co najmniej jeden obiekt terenu - tmp->pCenter = Math3D::vector3( 0.0, 0.0, 0.0 ); // enforce placement in the world center - tmp->iCount = Global::pTerrainCompact->TerrainCount() + 1; // zliczenie submodeli - tmp->nNode = new TGroundNode[ tmp->iCount ]; // sztuczne node dla kwadratów - tmp->nNode[ 0 ].iType = TP_MODEL; // pierwszy zawiera model (dla delete) - tmp->nNode[ 0 ].Model = Global::pTerrainCompact; - tmp->nNode[ 0 ].iFlags = 0x200; // nie wyświetlany, ale usuwany - for( int i = 1; i < tmp->iCount; ++i ) { // a reszta to submodele - tmp->nNode[ i ].iType = TP_SUBMODEL; - tmp->nNode[ i ].smTerrain = Global::pTerrainCompact->TerrainSquare( i - 1 ); - tmp->nNode[ i ].iFlags = 0x10; // nieprzezroczyste; nie usuwany - tmp->nNode[ i ].bVisible = true; - tmp->nNode[ i ].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce - } - } - else if( !tmp->asName.empty() ) // jest pusta gdy "none" - { // dodanie do wyszukiwarki - if( false == m_nodemap.Add( TP_MODEL, tmp->asName, tmp ) ) { - // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) - ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat - } - } -#else if( rmin < 0 ) { // legacy leftover: special case, terrain provided as 3d model tmp->iType = TP_TERRAIN; @@ -1274,6 +1229,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } else { +#ifdef EU07_USE_OLD_GROUNDCODE // regular 3d model parser->getTokens( 3 ); *parser @@ -1304,10 +1260,11 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat } } - } #endif + } break; } +#ifdef EU07_USE_OLD_GROUNDCODE // case TP_GEOMETRY : case GL_TRIANGLES: case GL_TRIANGLE_STRIP: @@ -1510,6 +1467,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } // koniec wczytywania trójkątów break; } +#endif case GL_LINES: case GL_LINE_STRIP: case GL_LINE_LOOP: { @@ -1802,7 +1760,7 @@ bool TGround::Init(std::string File) else { // jeśli jest pojazdem if( ( LastNode->DynamicObject->Mechanik != nullptr ) && ( LastNode->DynamicObject->Mechanik->Primary() ) ) { - // jeśli jest głównym (pasażer nie jest) + // jeśli jest głównym (pasażer nie jest) nTrainSetDriver = LastNode; // pojazd, któremu zostanie wysłany rozkład } LastNode->nNext = nRootDynamic; @@ -1859,8 +1817,7 @@ bool TGround::Init(std::string File) if (nTrainSetDriver) // pojazd, któremu zostanie wysłany rozkład { // wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy nTrainSetDriver->DynamicObject->Mechanik->DirectionInitial(); - nTrainSetDriver->DynamicObject->Mechanik->PutCommand("Timetable:" + asTrainName, - fTrainSetVel, 0, NULL); + nTrainSetDriver->DynamicObject->Mechanik->PutCommand("Timetable:" + asTrainName, fTrainSetVel, 0, NULL); } } if( LastNode ) { @@ -1926,7 +1883,7 @@ bool TGround::Init(std::string File) { ErrorLog("Duplicated event: " + tmp->asName); found->Append(tmp); // doczepka (taki wirtualny multiple bez warunków) - found->Type = tp_Ignored; // dezaktywacja pierwotnego - taka proteza na + found->m_ignored = true; // dezaktywacja pierwotnego - taka proteza na // wsteczną zgodność // SafeDelete(tmp); //bezlitośnie usuwamy wszelkie duplikaty, żeby nie // zaśmiecać drzewka @@ -1938,7 +1895,7 @@ bool TGround::Init(std::string File) RootEvent = tmp; if (!found) { // jeśli nazwa wystąpiła, to do kolejki i wyszukiwarki dodawany jest tylko pierwszy - if( ( RootEvent->Type != tp_Ignored ) + if( ( RootEvent->m_ignored == false ) && ( RootEvent->asName.find( "onstart" ) != std::string::npos ) ) { // event uruchamiany automatycznie po starcie AddToQuery( RootEvent, NULL ); // dodanie do kolejki @@ -2217,7 +2174,7 @@ bool TGround::InitEvents() } else { // nie ma komórki, to nie będzie działał poprawnie - Current->Type = tp_Ignored; // deaktywacja + Current->m_ignored = true; // deaktywacja ErrorLog("Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); } break; @@ -2241,7 +2198,7 @@ bool TGround::InitEvents() } else { // nie ma komórki, to nie będzie działał poprawnie - Current->Type = tp_Ignored; // deaktywacja + Current->m_ignored = true; // deaktywacja ErrorLog("Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); } break; @@ -2637,7 +2594,7 @@ void TGround::InitTraction() nTemp = new TGroundNode(); nTemp->iType = TP_TRACTIONPOWERSOURCE; nTemp->asName = Traction->asPowerSupplyName; - nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp); + nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp->asName); nTemp->psTractionPowerSource->Init(Traction->NominalVoltage, Traction->MaxCurrent); nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany dołączamy na końcu // nowego @@ -2826,8 +2783,11 @@ void TGround::TrackJoin(TGroundNode *Current) int iConnection; if (!Track->CurrentPrev()) { - tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, - Current); // Current do pominięcia +#ifdef EU07_USE_OLD_GROUNDCODE + tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, Current); // Current do pominięcia +#else + std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_0(), Track ); +#endif switch (iConnection) { case 0: @@ -2840,7 +2800,11 @@ void TGround::TrackJoin(TGroundNode *Current) } if (!Track->CurrentNext()) { +#ifdef EU07_USE_OLD_GROUNDCODE tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_1(), iConnection, Current); +#else + std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_1(), Track ); +#endif switch (iConnection) { case 0: @@ -2892,6 +2856,7 @@ bool TGround::InitLaunchers() 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; @@ -2978,49 +2943,36 @@ TTraction * TGround::TractionNearestFind(glm::dvec3 &p, int dir, TGroundNode *n) if ((sr = FastGetSubRect(c + i, r + j)) != NULL) // o ile w ogóle sektor jest for (nCurrent = sr->nRenderWires; nCurrent; nCurrent = nCurrent->nNext3) if (nCurrent->iType == TP_TRACTION) - if (nCurrent->hvTraction->psSection == - n->hvTraction->psSection) // jeśli ta sama sekcja + if (nCurrent->hvTraction->psSection == n->hvTraction->psSection) // jeśli ta sama sekcja if (nCurrent != n) // ale nie jest tym samym - if (nCurrent->hvTraction != - n->hvTraction - ->hvNext[0]) // ale nie jest bezpośrednio podłączonym + if (nCurrent->hvTraction != n->hvTraction->hvNext[0]) // ale nie jest bezpośrednio podłączonym if (nCurrent->hvTraction != n->hvTraction->hvNext[1]) if (nCurrent->hvTraction->psPower [k = (glm::dot( n->hvTraction->vParametric, nCurrent->hvTraction->vParametric) >= 0 ? dir ^ 1 : - dir)]) // ma zasilanie z odpowiedniej - // strony - if (nCurrent->hvTraction->fResistance[k] >= - 0.0) //żeby się nie propagowały jakieś ujemne - { // znaleziony kandydat do połączenia + dir)]) // ma zasilanie z odpowiedniej strony + if (nCurrent->hvTraction->fResistance[k] >= 0.0) { // żeby się nie propagowały jakieś ujemne + // znaleziony kandydat do połączenia d = glm::length2( p - glm::dvec3{ nCurrent->pCenter } ); // kwadrat odległości środków - if (dist > d) - { // zapamiętanie nowego najbliższego + if (dist > d) { + // zapamiętanie nowego najbliższego dist = d; // nowy rekord odległości nBest = nCurrent; - zg = k; // z którego końca brać wskaźnik - // zasilacza + zg = k; // z którego końca brać wskaźnik zasilacza } } - if (nBest) // jak znalezione przęsło z zasilaniem, to podłączenie "równoległe" - { - n->hvTraction->ResistanceCalc(dir, nBest->hvTraction->fResistance[zg], - nBest->hvTraction->psPower[zg]); - // testowo skrzywienie przęsła tak, aby pokazać skąd ma zasilanie - // if (dir) //1 gdy ciąg dalszy jest od strony Point2 - // n->hvTraction->pPoint3=0.25*(nBest->pCenter+3*(zg?nBest->hvTraction->pPoint4:nBest->hvTraction->pPoint3)); - // else - // n->hvTraction->pPoint4=0.25*(nBest->pCenter+3*(zg?nBest->hvTraction->pPoint4:nBest->hvTraction->pPoint3)); + if (nBest) { + // jak znalezione przęsło z zasilaniem, to podłączenie "równoległe" + n->hvTraction->ResistanceCalc(dir, nBest->hvTraction->fResistance[zg], nBest->hvTraction->psPower[zg]); } return (nBest ? nBest->hvTraction : nullptr); }; -#ifdef EU07_USE_OLD_GROUNDCODE bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) { - if( Event->bEnabled ) { + if( ( false == Event->m_ignored ) && ( true == Event->bEnabled ) ) { // jeśli może być dodany do kolejki (nie używany w skanowaniu) if( !Event->iQueued ) // jeśli nie dodany jeszcze do kolejki { // kolejka eventów jest posortowana względem (fStartTime) @@ -3169,8 +3121,8 @@ bool TGround::CheckQuery() } else // a jak nazwa jest unikalna, to kolejka idzie dalej QueryRootEvent = QueryRootEvent->evNext; // NULL w skrajnym przypadku - if (tmpEvent->bEnabled) - { // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć + if( ( false == tmpEvent->m_ignored ) && ( true == tmpEvent->bEnabled ) ) { + // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć --tmpEvent->iQueued; // teraz moze być ponownie dodany do kolejki WriteLog( "EVENT LAUNCHED" + ( tmpEvent->Activator ? ( " by " + tmpEvent->Activator->asName ) : "" ) + ": " + tmpEvent->asName ); switch (tmpEvent->Type) @@ -3222,7 +3174,7 @@ bool TGround::CheckQuery() // loc.Z= tmpEvent->Params[8].nGroundNode->pCenter.y; if (Global::iMultiplayer) // potwierdzenie wykonania dla serwera (odczyt // semafora już tak nie działa) - WyslijEvent(tmpEvent->asName, tmpEvent->Activator->GetName()); + WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); // tmpEvent->Params[9].asMemCell->PutCommand(tmpEvent->Activator->Mechanik,loc); tmpEvent->Params[9].asMemCell->PutCommand( tmpEvent->Activator->Mechanik, &tmpEvent->Params[8].nGroundNode->pCenter); @@ -3355,7 +3307,7 @@ bool TGround::CheckQuery() 0) // jednoznaczne tylko, gdy nie było else { if (tmpEvent->Activator) - WyslijEvent(tmpEvent->asName, tmpEvent->Activator->GetName()); + WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); else WyslijEvent(tmpEvent->asName, ""); } @@ -3468,7 +3420,6 @@ bool TGround::CheckQuery() } // while return true; } -#endif void TGround::UpdatePhys(double dt, int iter) { // aktualizacja fizyki stałym krokiem: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń @@ -3587,9 +3538,8 @@ 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 @@ -3709,35 +3659,29 @@ bool TGround::GetTraction(TDynamicObject *model) { // dany sektor może nie mieć nic w środku for (node = tmp->nRenderWires; node; node = node->nNext3) // następny z grupy - if (node->iType == - TP_TRACTION) // w grupie tej są druty oraz inne linie + if (node->iType == TP_TRACTION) // w grupie tej są druty oraz inne linie { - vParam = - node->hvTraction - ->vParametric; // współczynniki równania parametrycznego + // współczynniki równania parametrycznego + vParam = node->hvTraction->vParametric; fRaParam = -glm::dot(pant0, vFront); auto const paramfrontdot = glm::dot( vParam, vFront ); fRaParam = -( glm::dot( node->hvTraction->pPoint1, vFront ) + fRaParam ) / ( paramfrontdot != 0.0 ? paramfrontdot : 0.001 ); // div0 trap if ((fRaParam >= -0.001) ? (fRaParam <= 1.001) : false) - { // jeśli tylko jest w przedziale, wyznaczyć odległość wzdłuż - // wektorów vUp i vLeft - vStyk = node->hvTraction->pPoint1 + - fRaParam * vParam; // punkt styku płaszczyzny z - // drutem (dla generatora łuku - // el.) - vGdzie = vStyk - pant0; // wektor - fVertical = glm::dot( - vGdzie, - vUp); // musi się mieścić w przedziale ruchu pantografu + { // 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.) + vStyk = node->hvTraction->pPoint1 + fRaParam * vParam; + // wektor musi się mieścić w przedziale ruchu pantografu + vGdzie = vStyk - pant0; + fVertical = glm::dot( vGdzie, vUp); if (fVertical >= 0.0) // jeśli ponad pantografem (bo może // łapać druty spod wiaduktu) if (Global::bEnableTraction ? fVertical < p->PantWys - 0.15 : - false) // jeśli drut jest niżej niż 15cm pod - // ślizgiem - { // przełączamy w tryb połamania, o ile jedzie; + false) { + // 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 @@ -3827,18 +3771,7 @@ bool TGround::GetTraction(TDynamicObject *model) else p->hvPowerWire = NULL; // pantograf opuszczony } - // if (model->fWahaczeAmpMoverParameters->DistCounter) - //{//nieużywana normalnie zmienna ogranicza powtórzone logowania - // model->fWahaczeAmp=model->MoverParameters->DistCounter; - // ErrorLog(FloatToStrF(1000.0*model->MoverParameters->DistCounter,ffFixed,7,3)+","+FloatToStrF(p->PantTraction,ffFixed,7,3)+","+FloatToStrF(p->fHorizontal,ffFixed,7,3)+","+FloatToStrF(p->PantWys,ffFixed,7,3)+","+AnsiString(p->hvPowerWire?1:0)); - // // - // if (p->fHorizontal>1.0) - //{ - // //Global::iPause|=1; //zapauzowanie symulacji - // Global::fTimeSpeed=1; //spowolnienie czasu do obejrzenia pantografu - // return true; //łapacz - //} - //} + return true; }; diff --git a/Ground.h b/Ground.h index f045c23b..00bf072b 100644 --- a/Ground.h +++ b/Ground.h @@ -285,20 +285,24 @@ class TGround 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); +#endif TGroundNode * AddGroundNode(cParser *parser); +#ifdef EU07_USE_OLD_GROUNDCODE 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(); -#endif TGroundNode * DynamicFindAny(std::string const &Name); TGroundNode * DynamicFind(std::string const &Name); +#endif void DynamicList(bool all = false); TGroundNode * FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType); TGroundRect * GetRect( double x, double z ); @@ -325,7 +329,9 @@ class TGround // convert tp_terrain model to a series of triangle nodes void convert_terrain( TGroundNode const *Terrain ); void convert_terrain( TSubModel const *Submodel ); +#ifdef EU07_USE_OLD_GROUNDCODE void RaTriangleDivider(TGroundNode *node); +#endif void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam); public: diff --git a/MemCell.cpp b/MemCell.cpp index 0bf32bf3..8143b2fa 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -180,3 +180,15 @@ void TMemCell::AssignEvents(TEvent *e) { // powiązanie eventu OnSent = e; }; + + + +// legacy method, initializes traction after deserialization from scenario file +void +memory_table::InitCells() { + + for( auto *cell : m_items ) { + // Ra: eventy komórek pamięci, wykonywane po wysłaniu komendy do zatrzymanego pojazdu + cell->AssignEvents( simulation::Events.FindEvent( cell->name() + ":sent" ) ); + } +} diff --git a/MemCell.h b/MemCell.h index 35637431..aa84d3b9 100644 --- a/MemCell.h +++ b/MemCell.h @@ -61,8 +61,14 @@ class TMemCell : public editor::basic_node { void AssignEvents(TEvent *e); }; -class memory_manager : public basic_table { + +class memory_table : public basic_table { + +public: + // legacy method, initializes traction after deserialization from scenario file + void + InitCells(); }; //--------------------------------------------------------------------------- diff --git a/Model3d.cpp b/Model3d.cpp index ea6dd7b9..f2ce05be 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1641,7 +1641,7 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, } } else { - ErrorLog( "Bad model: reference to non-existent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) ); + ErrorLog( "Bad model: reference to nonexistent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) ); m_material = null_handle; } } diff --git a/RealSound.cpp b/RealSound.cpp index ce9c6a3b..f16ffe05 100644 --- a/RealSound.cpp +++ b/RealSound.cpp @@ -21,8 +21,7 @@ http://mozilla.org/MPL/2.0/. #include "mczapkie/mctools.h" #include "usefull.h" -TRealSound::TRealSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, - bool freqmod, double rmin) +TRealSound::TRealSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) { Init(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin); } @@ -32,12 +31,7 @@ TRealSound::~TRealSound() // if (this) if (pSound) pSound->Stop(); } -void TRealSound::Free() -{ -} - -void TRealSound::Init(std::string const &SoundName, double DistanceAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod, double rmin) +void TRealSound::Init(std::string const &SoundName, double DistanceAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) { // Nazwa=SoundName; //to tak raczej nie zadziała, (SoundName) jest tymczasowe pSound = TSoundsManager::GetFromName(SoundName, Dynamic, &fFrequency); @@ -71,7 +65,7 @@ void TRealSound::Init(std::string const &SoundName, double DistanceAttenuation, dSoundAtt = -1; }; -double TRealSound::ListenerDistance(vector3 ListenerPosition) +double TRealSound::ListenerDistance( Math3D::vector3 ListenerPosition) { if (dSoundAtt == -1) { @@ -83,7 +77,7 @@ double TRealSound::ListenerDistance(vector3 ListenerPosition) } } -void TRealSound::Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition) +void TRealSound::Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition) { if (!pSound) return; @@ -155,11 +149,6 @@ void TRealSound::Play(double Volume, int Looping, bool ListenerInside, vector3 N } }; -void TRealSound::Start(){ - // włączenie dźwięku - -}; - void TRealSound::Stop() { DWORD stat; @@ -231,17 +220,14 @@ void TRealSound::ResetPosition() pSound->SetCurrentPosition(0); } -TTextSound::TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod, double rmin) - : TRealSound(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin) +TTextSound::TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) : + TRealSound(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin) { Init(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin); } -void TTextSound::Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod, double rmin) +void TTextSound::Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) { // dodatkowo doczytuje plik tekstowy - //TRealSound::Init(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin); fTime = GetWaveTime(); std::string txt(SoundName); txt.erase( txt.rfind( '.' ) ); // obcięcie rozszerzenia @@ -253,15 +239,11 @@ void TTextSound::Init(std::string const &SoundName, double SoundAttenuation, dou txt = "sounds\\" + txt; //ścieżka może nie być podana if (FileExists(txt)) { // wczytanie -/* TFileStream *ts = new TFileStream(txt, fmOpenRead); - asText = AnsiString::StringOfChar(' ', ts->Size); - ts->Read(asText.c_str(), ts->Size); - delete ts; -*/ std::ifstream inputfile( txt ); + std::ifstream inputfile( txt ); asText.assign( std::istreambuf_iterator( inputfile ), std::istreambuf_iterator() ); } }; -void TTextSound::Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition) +void TTextSound::Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition) { if (false == asText.empty()) { // jeśli ma powiązany tekst diff --git a/RealSound.h b/RealSound.h index 1eec3761..c72d76a0 100644 --- a/RealSound.h +++ b/RealSound.h @@ -7,24 +7,25 @@ obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef RealSoundH -#define RealSoundH +#pragma once #include -#include "Sound.h" -#include "Geometry.h" -class TRealSound -{ - protected: +#include "Sound.h" +#include "dumb3d.h" +#include "names.h" + +class TRealSound { + +protected: PSound pSound = nullptr; -// char *Nazwa; // dla celow odwszawiania NOTE: currently not used anywhere - double fDistance = 0.0, - fPreviousDistance = 0.0; // dla liczenia Dopplera + double fDistance = 0.0; + double fPreviousDistance = 0.0; // dla liczenia Dopplera float fFrequency = 22050.0; // częstotliwość samplowania pliku int iDoppler = 0; // Ra 2014-07: możliwość wyłączenia efektu Dopplera np. dla śpiewu ptaków - public: - vector3 vSoundPosition; // polozenie zrodla dzwieku +public: + std::string m_name; + Math3D::vector3 vSoundPosition; // polozenie zrodla dzwieku double dSoundAtt = -1.0; // odleglosc polowicznego zaniku dzwieku double AM = 0.0; // mnoznik amplitudy double AA = 0.0; // offset amplitudy @@ -32,37 +33,43 @@ class TRealSound double FA = 0.0; // offset czestotliwosci bool bLoopPlay = false; // czy zapętlony dźwięk jest odtwarzany TRealSound() = default; - TRealSound( std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, - bool freqmod = false, double rmin = 0.0); + TRealSound( std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0 ); ~TRealSound(); - void Free(); - void Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, - bool freqmod = false, double rmin = 0.0); - double ListenerDistance(vector3 ListenerPosition); - void Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition); - void Start(); + void Init( std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0 ); + double ListenerDistance( Math3D::vector3 ListenerPosition); + void Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition); void Stop(); void AdjFreq(double Freq, double dt); void SetPan(int Pan); double GetWaveTime(); // McZapkie TODO: dorobic dla roznych bps int GetStatus(); void ResetPosition(); - // void FreqReset(float f=22050.0) {fFrequency=f;}; bool Empty() { return ( pSound == nullptr ); } + void + name( std::string Name ) { + m_name = Name; } + std::string const & + name() const { + return m_name; } + glm::dvec3 + location() const { + return vSoundPosition; }; }; -class TTextSound : public TRealSound -{ // dźwięk ze stenogramem + + +class TTextSound : public TRealSound { + // dźwięk ze stenogramem std::string asText; float fTime; // czas trwania - public: - TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod = false, double rmin = 0.0); - void Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod = false, double rmin = 0.0); - void Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition); +public: + TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0); + void Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0); + void Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition); }; + + class TSynthSound { // klasa generująca sygnał odjazdu (Rp12, Rp13), potem rozbudować o pracę manewrowego... int iIndex[44]; // indeksy początkowe, gdy mamy kilka wariantów dźwięków składowych @@ -74,10 +81,16 @@ class TSynthSound // 41 - "tysiące" // 42 - indeksy początkowe dla "odjazd" // 43 - indeksy początkowe dla "gotów" - PSound *sSound; // posortowana tablica dźwięków, rozmiar zależny od liczby znalezionych plików + PSound sSound; // posortowana tablica dźwięków, rozmiar zależny od liczby znalezionych plików // a może zamiast wielu plików/dźwięków zrobić jeden połączony plik i posługiwać się czasem // od..do? }; + + +// collection of generators for power grid present in the scene +class sound_table : public basic_table { + +}; + //--------------------------------------------------------------------------- -#endif diff --git a/Segment.cpp b/Segment.cpp index bfed769e..293daf57 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -103,7 +103,7 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M fStep = fNewStep; if (fLength <= 0) { - ErrorLog( "Bad geometry: zero length spline \"" + pOwner->NameGet() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); + ErrorLog( "Bad geometry: zero length spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); fLength = 0.01; // crude workaround TODO: fix this properly /* return false; // zerowe nie mogą być @@ -205,7 +205,7 @@ double TSegment::GetTFromS(double const s) const // Newton's method failed. If this happens, increase iterations or // tolerance or integration accuracy. // return -1; //Ra: tu nigdy nie dojdzie - ErrorLog( "Bad geometry: shape estimation failed for spline \"" + pOwner->NameGet() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); + ErrorLog( "Bad geometry: shape estimation failed for spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); // MessageBox(0,"Too many iterations","GetTFromS",MB_OK); return fTime; }; @@ -323,23 +323,28 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const } }; -void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const -{ // ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy - if (bCurve) - { // można by wprowadzić uproszczony wzór dla okręgów płaskich - double t = GetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t) - p = RaInterpolate(t); - a.x = (1.0 - t) * fRoll1 + (t)*fRoll2; // przechyłka w danym miejscu (zmienia się liniowo) +// ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy +void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const { + + if (bCurve) { + // można by wprowadzić uproszczony wzór dla okręgów płaskich + auto const t = GetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t) + p = FastGetPoint( t ); + // przechyłka w danym miejscu (zmienia się liniowo) + a.x = interpolate( fRoll1, fRoll2, t ); // pochodna jest 3*A*t^2+2*B*t+C - a.y = atan(t * (t * 3.0 * vA.y + vB.y + vB.y) + vC.y); // pochylenie krzywej (w pionie) - a.z = -atan2(t * (t * 3.0 * vA.x + vB.x + vB.x) + vC.x, - t * (t * 3.0 * vA.z + vB.z + vB.z) + vC.z); // kierunek krzywej w planie + auto const tangent = t * ( t * 3.0 * vA + vB + vB ) + vC; + // pochylenie krzywej (w pionie) + a.y = std::atan( tangent.y ); + // kierunek krzywej w planie + a.z = -std::atan2( tangent.x, tangent.z ); } - else - { // wyliczenie dla odcinka prostego jest prostsze - double t = fDistance / fLength; // zerowych torów nie ma - p = ((1.0 - t) * Point1 + (t)*Point2); - a.x = (1.0 - t) * fRoll1 + (t)*fRoll2; // przechyłka w danym miejscu (zmienia się liniowo) + else { + // wyliczenie dla odcinka prostego jest prostsze + auto const t = fDistance / fLength; // zerowych torów nie ma + p = FastGetPoint( t ); + // przechyłka w danym miejscu (zmienia się liniowo) + a.x = interpolate( fRoll1, fRoll2, t ); a.y = fStoop; // pochylenie toru prostego a.z = fDirection; // kierunek toru w planie } diff --git a/Segment.h b/Segment.h index 93209804..dfcf09be 100644 --- a/Segment.h +++ b/Segment.h @@ -14,37 +14,6 @@ http://mozilla.org/MPL/2.0/. #include "Classes.h" #include "usefull.h" -// 110405 Ra: klasa punktów przekroju z normalnymi -/* -class vector6 : public Math3D::vector3 -{ // punkt przekroju wraz z wektorem normalnym - public: - Math3D::vector3 n; - vector6() - { - x = y = z = n.x = n.z = 0.0; - n.y = 1.0; - }; - vector6(double a, double b, double c, double d, double e, double f) - { - x = a; - y = b; - z = c; - n.x = 0.0; - n.y = 1.0; - n.z = 0.0; - }; // Ra: bo na razie są z tym problemy - vector6(double a, double b, double c) - { - x = a; - y = b; - z = c; - n.x = 0.0; - n.y = 1.0; - n.z = 0.0; - }; -}; -*/ class TSegment { // aproksymacja toru (zwrotnica ma dwa takie, jeden z nich jest aktywny) private: @@ -60,7 +29,6 @@ class TSegment double fStoop = 0.0; // Ra: kąt wzniesienia; dla łuku od Point1 Math3D::vector3 vA, vB, vC; // współczynniki wielomianów trzeciego stopnia vD==Point1 TTrack *pOwner = nullptr; // wskaźnik na właściciela - double fAngle[ 2 ] = { 0.0, 0.0 }; // kąty zakończenia drogi na przejazdach Math3D::vector3 GetFirstDerivative(double const fTime) const; @@ -140,10 +108,6 @@ public: int RaSegCount() const { return fTsBuffer ? iSegCount : 1; }; - inline - void - AngleSet(int const i, double const a) { - fAngle[i] = a; }; }; //--------------------------------------------------------------------------- diff --git a/Track.cpp b/Track.cpp index 041842bf..04884ce7 100644 --- a/Track.cpp +++ b/Track.cpp @@ -26,6 +26,7 @@ http://mozilla.org/MPL/2.0/. #include "AnimModel.h" #include "MemCell.h" #include "Event.h" +#include "simulation.h" // 101206 Ra: trapezoidalne drogi i tory // 110720 Ra: rozprucie zwrotnicy i odcinki izolowane @@ -2651,6 +2652,31 @@ int TTrack::TestPoint(vector3 *Point) return -1; }; +// retrieves list of the track's end points +std::vector +TTrack::endpoints() const { + + switch( eType ) { + case tt_Normal: + case tt_Table: { + return { + glm::dvec3{ Segment->FastGetPoint_0() }, + glm::dvec3{ Segment->FastGetPoint_1() } }; + } + case tt_Switch: + case tt_Cross: { + return { + glm::dvec3{ SwitchExtension->Segments[ 0 ]->FastGetPoint_0() }, + glm::dvec3{ SwitchExtension->Segments[ 0 ]->FastGetPoint_1() }, + glm::dvec3{ SwitchExtension->Segments[ 1 ]->FastGetPoint_0() }, + glm::dvec3{ SwitchExtension->Segments[ 1 ]->FastGetPoint_1() } }; + } + default: { + return{}; + } + } +} + void TTrack::MovedUp1(float const dh) { // poprawienie przechyłki wymaga wydłużenia podsypki fTexHeight1 += dh; @@ -2745,11 +2771,11 @@ TTrack * TTrack::Connected(int s, double &d) const // legacy method, initializes tracks after deserialization from scenario file void path_table::InitTracks() { -/* - TGroundNode *Model; - int iConnection; - for( auto *track : m_paths ) { + int connection { -1 }; + TTrack *matchingtrack { nullptr }; + + for( auto *track : m_items ) { track->AssignEvents( simulation::Events.FindEvent( track->asEvent0Name ), @@ -2776,30 +2802,32 @@ path_table::InitTracks() { } switch (track->eType) { +/* + // TODO: re-enable case tt_Table: { // obrotnicę też łączymy na starcie z innymi torami // szukamy modelu o tej samej nazwie - Model = FindGroundNode(Current->asName, TP_MODEL); + auto *instance = simulation::Instances.find( trackname ); // wiązanie toru z modelem obrotnicy track->RaAssign( Current, - ( Model ? - Model->Model : - nullptr ), + instance, simulation::Events.FindEvent( trackname + ":done" ), simulation::Events.FindEvent( trackname + ":joined" ) ); - if( Model == nullptr ) { + if( instance == nullptr ) { // jak nie ma modelu to pewnie jest wykolejnica, a ta jest domyślnie zamknięta i wykoleja break; } + // no break on purpose: // jak coś pójdzie źle, to robimy z tego normalny tor } +*/ case tt_Normal: { // tylko proste są podłączane do rozjazdów, stąd dwa rozjazdy się nie połączą ze sobą if( track->CurrentPrev() == nullptr ) { // tylko jeśli jeszcze nie podłączony - auto *matchingtrack = simulation::Region.FindTrack( track->CurrentSegment()->FastGetPoint_0(), iConnection, track ); - switch( iConnection ) { + std::tie( matchingtrack, connection ) = simulation::Region->find_path( track->CurrentSegment()->FastGetPoint_0(), track ); + switch( connection ) { case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków // if ((Track->iCategoryFlag&1)==0) //jeśli nie jest torem szynowym // Track->ConnectPrevPrev(Track,0); //łączenie końca odcinka do samego siebie @@ -2830,12 +2858,14 @@ path_table::InitTracks() { matchingtrack->SetConnections( 1 ); // robi też Switch(0) matchingtrack->Switch( 0 ); break; + default: + break; } } if( track->CurrentNext() == nullptr ) { // tylko jeśli jeszcze nie podłączony - auto *matchingtrack = simulation::Region.FindTrack( track->CurrentSegment()->FastGetPoint_1(), iConnection, track ); - switch( iConnection ) { + std::tie( matchingtrack, connection ) = simulation::Region->find_path( track->CurrentSegment()->FastGetPoint_1(), track ); + switch( connection ) { case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków // if ((Track->iCategoryFlag&1)==0) //jeśli nie jest torem szynowym // Track->ConnectNextNext(Track,1); //łączenie końca odcinka do samego siebie @@ -2866,6 +2896,8 @@ path_table::InitTracks() { matchingtrack->SetConnections( 1 ); // robi też Switch(0) // tmp->Switch(0); break; + default: + break; } } break; @@ -2910,11 +2942,12 @@ path_table::InitTracks() { else { // utworzenie automatycznej komórki // TODO: determine suitable location for this one, create and add world reference node - auto *memorycell = new TMemCell( isolated->asName ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy + scene::node_data nodedata; + nodedata.name = isolated->asName; + auto *memorycell = new TMemCell( nodedata ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy simulation::Memory.insert( memorycell ); isolated->pMemCell = memorycell; // wskaźnik komóki przekazany do odcinka izolowanego } isolated = isolated->Next(); } -*/ } diff --git a/Track.h b/Track.h index fe9d4e1c..0aaac064 100644 --- a/Track.h +++ b/Track.h @@ -119,7 +119,6 @@ class TTrack : public editor::basic_node { friend class opengl_renderer; private: -// TGroundNode * pMyNode = nullptr; // Ra: proteza, żeby tor znał swoją nazwę TODO: odziedziczyć TTrack z TGroundNode TIsolated * pIsolated = nullptr; // obwód izolowany obsługujący zajęcia/zwolnienia grupy torów std::shared_ptr SwitchExtension; // dodatkowe dane do toru, który jest zwrotnicą std::shared_ptr Segment; @@ -234,6 +233,9 @@ public: void origin( glm::dvec3 Origin ) { m_origin = Origin; } + // retrieves list of the track's end points + std::vector + endpoints() const; #ifdef EU07_USE_OLD_GROUNDCODE void create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ); // wypełnianie VBO diff --git a/Traction.cpp b/Traction.cpp index dccce473..75686a97 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -91,7 +91,6 @@ sekcji z sąsiedniego przęsła). */ TTraction::TTraction( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} - // legacy constructor TTraction::TTraction( std::string Name ) { @@ -173,6 +172,13 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) { m_location = interpolate( pPoint2, pPoint1, 0.5 ); } +// retrieves list of the track's end points +std::vector +TTraction::endpoints() const { + + return { pPoint1, pPoint2 }; +} + std::size_t #ifdef EU07_USE_OLD_GROUNDCODE TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { @@ -621,205 +627,213 @@ TTraction::wire_color() const { // legacy method, initializes traction after deserialization from scenario file void traction_table::InitTraction() { -/* + //łączenie drutów ze sobą oraz z torami i eventami // TGroundNode *nCurrent, *nTemp; // TTraction *tmp; // znalezione przęsło - int iConnection; - std::string name; + + int connection { -1 }; + TTraction *matchingtraction { nullptr }; + for( auto *traction : m_items ) { // podłączenie do zasilacza, żeby można było sumować prąd kilku pojazdów // a jednocześnie z jednego miejsca zmieniać napięcie eventem // wykonywane najpierw, żeby można było logować podłączenie 2 zasilaczy do jednego drutu // izolator zawieszony na przęśle jest ma być osobnym odcinkiem drutu o długości ok. 1m, // podłączonym do zasilacza o nazwie "*" (gwiazka); "none" nie będzie odpowiednie - nTemp = FindGroundNode(traction->asPowerSupplyName, TP_TRACTIONPOWERSOURCE); - if (nTemp) // jak zasilacz znaleziony - traction->PowerSet(nTemp->psTractionPowerSource); // to podłączyć do przęsła - else if (traction->asPowerSupplyName != "*") // gwiazdka dla przęsła z izolatorem - if (traction->asPowerSupplyName != "none") // dopuszczamy na razie brak podłączenia? - { // logowanie błędu i utworzenie zasilacza o domyślnej zawartości - ErrorLog("Missed TractionPowerSource: " + traction->asPowerSupplyName); - nTemp = new TGroundNode(); - nTemp->iType = TP_TRACTIONPOWERSOURCE; - nTemp->asName = traction->asPowerSupplyName; - nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp); - nTemp->psTractionPowerSource->Init(traction->NominalVoltage, traction->MaxCurrent); - nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany dołączamy na końcu - // nowego - nRootOfType[nTemp->iType] = nTemp; // ustawienie nowego na początku listy + auto *powersource = simulation::Powergrid.find( traction->asPowerSupplyName ); + if( powersource ) { + // jak zasilacz znaleziony to podłączyć do przęsła + traction->PowerSet( powersource ); + } + else { + if( ( traction->asPowerSupplyName != "*" ) // gwiazdka dla przęsła z izolatorem + && ( traction->asPowerSupplyName != "none" ) ) { // dopuszczamy na razie brak podłączenia? + // logowanie błędu i utworzenie zasilacza o domyślnej zawartości + ErrorLog( "Bad scenario: traction piece connected to nonexistent power source \"" + traction->asPowerSupplyName + "\"" ); + scene::node_data nodedata; + nodedata.name = traction->asPowerSupplyName; + powersource = new TTractionPowerSource( nodedata ); + powersource->Init( traction->NominalVoltage, traction->MaxCurrent ); + simulation::Powergrid.insert( powersource ); } + } } for( auto *traction : m_items ) { - if (!traction->hvNext[0]) // tylko jeśli jeszcze nie podłączony - { - tmp = FindTraction(traction->pPoint1, iConnection, nCurrent); - switch (iConnection) - { - case 0: - traction->Connect(0, tmp, 0); - break; - case 1: - traction->Connect(0, tmp, 1); - break; + if (!traction->hvNext[0]) { + // tylko jeśli jeszcze nie podłączony + std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint1, traction ); + switch (connection) { + case 0: { + traction->Connect( 0, matchingtraction, 0 ); + break; + } + case 1: { + traction->Connect( 0, matchingtraction, 1 ); + break; + } + default: { + break; + } } - if (traction->hvNext[0]) // jeśli został podłączony - if (traction->psSection && tmp->psSection) // tylko przęsło z izolatorem może nie - // mieć zasilania, bo ma 2, trzeba - // sprawdzać sąsiednie - if (traction->psSection != - tmp->psSection) // połączone odcinki mają różne zasilacze - { // to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo - // błąd - if (traction->psSection->bSection && !tmp->psSection->bSection) - { //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji - tmp->PowerSet(traction->psSection); // zastąpienie wskazaniem sekcji + if( traction->hvNext[ 0 ] ) { + // jeśli został podłączony + if( ( traction->psSection != nullptr ) + && ( matchingtraction->psSection != nullptr ) ) { + // tylko przęsło z izolatorem może nie mieć zasilania, bo ma 2, trzeba sprawdzać sąsiednie + if( traction->psSection != matchingtraction->psSection ) { + // połączone odcinki mają różne zasilacze + // to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo błąd + if( ( true == traction->psSection->bSection ) + && ( false == matchingtraction->psSection->bSection ) ) { + //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji + matchingtraction->PowerSet( traction->psSection ); // zastąpienie wskazaniem sekcji } - else if (!traction->psSection->bSection && tmp->psSection->bSection) - { //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji - traction->PowerSet(tmp->psSection); // zastąpienie wskazaniem sekcji + else if( ( false == traction->psSection->bSection ) && + ( true == matchingtraction->psSection->bSection ) ) { + //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji + traction->PowerSet( matchingtraction->psSection ); // zastąpienie wskazaniem sekcji + } + else { + // jeśli obie to sekcje albo obie podstacje, to będzie błąd + ErrorLog( "Bad scenario: faulty traction power connection at location " + to_string( traction->pPoint1 ) ); } - else // jeśli obie to sekcje albo obie podstacje, to będzie błąd - ErrorLog("Bad power: at " + - to_string(traction->pPoint1.x, 2, 6) + " " + - to_string(traction->pPoint1.y, 2, 6) + " " + - to_string(traction->pPoint1.z, 2, 6)); } + } + } } - if (!traction->hvNext[1]) // tylko jeśli jeszcze nie podłączony - { - tmp = FindTraction(traction->pPoint2, iConnection, nCurrent); - switch (iConnection) - { - case 0: - traction->Connect(1, tmp, 0); - break; - case 1: - traction->Connect(1, tmp, 1); - break; + if (!traction->hvNext[1]) { + // tylko jeśli jeszcze nie podłączony + std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint2, traction ); + switch (connection) { + case 0: { + traction->Connect( 1, matchingtraction, 0 ); + break; + } + case 1: { + traction->Connect( 1, matchingtraction, 1 ); + break; + } + default: { + break; + } } - if (traction->hvNext[1]) // jeśli został podłączony - if (traction->psSection && tmp->psSection) // tylko przęsło z izolatorem może nie - // mieć zasilania, bo ma 2, trzeba - // sprawdzać sąsiednie - if (traction->psSection != tmp->psSection) - { // to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo - // błąd - if (traction->psSection->bSection && !tmp->psSection->bSection) - { //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji - tmp->PowerSet(traction->psSection); // zastąpienie wskazaniem sekcji + if( traction->hvNext[ 1 ] ) { + // jeśli został podłączony + if( ( traction->psSection != nullptr ) + && ( matchingtraction->psSection != nullptr ) ) { + // tylko przęsło z izolatorem może nie mieć zasilania, bo ma 2, trzeba sprawdzać sąsiednie + if( traction->psSection != matchingtraction->psSection ) { + // to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo błąd + if( ( true == traction->psSection->bSection ) + && ( false == matchingtraction->psSection->bSection ) ) { + //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji + matchingtraction->PowerSet( traction->psSection ); // zastąpienie wskazaniem sekcji } - else if (!traction->psSection->bSection && tmp->psSection->bSection) - { //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji - traction->PowerSet(tmp->psSection); // zastąpienie wskazaniem sekcji + else if( ( false == traction->psSection->bSection ) + && ( true == matchingtraction->psSection->bSection ) ) { + //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji + traction->PowerSet( matchingtraction->psSection ); // zastąpienie wskazaniem sekcji + } + else { + // jeśli obie to sekcje albo obie podstacje, to będzie błąd + ErrorLog( "Bad scenario: faulty traction power connection at location " + to_string( traction->pPoint2 ) ); } - else // jeśli obie to sekcje albo obie podstacje, to będzie błąd - ErrorLog("Bad power: at " + - to_string(traction->pPoint2.x, 2, 6) + " " + - to_string(traction->pPoint2.y, 2, 6) + " " + - to_string(traction->pPoint2.z, 2, 6)); } + } + } } } - iConnection = 0; // teraz będzie licznikiem końców + auto endcount { 0 }; for( auto *traction : m_items ) { // operacje mające na celu wykrywanie bieżni wspólnych i łączenie przęseł naprążania - if (traction->WhereIs()) // oznakowanie przedostatnich przęseł - { // poszukiwanie bieżni wspólnej dla przedostatnich przęseł, również w celu połączenia - // zasilania - // to się nie sprawdza, bo połączyć się mogą dwa niezasilane odcinki jako najbliższe - // sobie - // nCurrent->hvTraction->hvParallel=TractionNearestFind(nCurrent->pCenter,0,nCurrent); - // //szukanie najbliższego przęsła - // trzeba by zliczać końce, a potem wpisać je do tablicy, aby sukcesywnie podłączać do - // zasilaczy + if( traction->WhereIs() ) { + // true for outer pieces of the traction section traction->iTries = 5; // oznaczanie końcowych - ++iConnection; + ++endcount; } - if (traction->fResistance[0] == 0.0) - { - traction->ResistanceCalc(); // obliczanie przęseł w segmencie z bezpośrednim zasilaniem - // ErrorLog("Section "+nCurrent->hvTraction->asPowerSupplyName+" connected"); //jako - // niby błąd będzie bardziej widoczne + if (traction->fResistance[0] == 0.0) { + // obliczanie przęseł w segmencie z bezpośrednim zasilaniem + traction->ResistanceCalc(); traction->iTries = 0; // nie potrzeba mu szukać zasilania } - // if (!Traction->hvParallel) //jeszcze utworzyć pętle z bieżni wspólnych } - int zg = 0; // zgodność kierunku przęseł, tymczasowo iterator do tabeli końców - // końców jest ok. 10 razy mniej niż wszystkich przęseł (Quark: 216) - TGroundNode **nEnds = new TGroundNode *[iConnection]; + + std::vector ends; ends.reserve( endcount ); for( auto *traction : m_items ) { //łączenie bieżni wspólnych, w tym oznaczanie niepodanych jawnie - if (!traction->asParallel.empty()) // będzie wskaźnik na inne przęsło + if( false == traction->asParallel.empty() ) { + // będzie wskaźnik na inne przęsło if( ( traction->asParallel == "none" ) || ( traction->asParallel == "*" ) ) { // jeśli nieokreślone traction->iLast = 2; // jakby przedostatni - niech po prostu szuka (iLast już przeliczone) } - else if (!traction->hvParallel) // jeśli jeszcze nie został włączony w kółko - { + else if( traction->hvParallel == nullptr ) { + // jeśli jeszcze nie został włączony w kółko auto *nTemp = find( traction->asParallel ); - if (nTemp) - { // o ile zostanie znalezione przęsło o takiej nazwie - if (!nTemp->hvParallel) // jeśli tamten jeszcze nie ma wskaźnika bieżni wspólnej + if( nTemp != nullptr ) { + // o ile zostanie znalezione przęsło o takiej nazwie + if( nTemp->hvParallel == nullptr ) { + // jeśli tamten jeszcze nie ma wskaźnika bieżni wspólnej traction->hvParallel = nTemp; // wpisać siebie i dalej dać mu wskaźnik zwrotny - else // a jak ma, to albo dołączyć się do kółeczka + } + else { + // a jak ma, to albo dołączyć się do kółeczka traction->hvParallel = nTemp->hvParallel; // przjąć dotychczasowy wskaźnik od niego + } nTemp->hvParallel = traction; // i na koniec ustawienie wskaźnika zwrotnego } - if (!traction->hvParallel) - ErrorLog("Missed overhead: " + traction->asParallel); // logowanie braku - } - if (traction->iTries > 0) // jeśli zaznaczony do podłączenia - // if (!nCurrent->hvTraction->psPower[0]||!nCurrent->hvTraction->psPower[1]) - if (zg < iConnection) // zabezpieczenie - nEnds[zg++] = nCurrent; // wypełnianie tabeli końców w celu szukania im połączeń - } - while( zg < iConnection ) { - // zapełnienie do końca tablicy, jeśli by jakieś końce wypadły - nEnds[ zg++ ] = nullptr; - } - zg = 1; // nieefektywny przebieg kończy łączenie - while (zg) - { // ustalenie zastępczej rezystancji dla każdego przęsła - zg = 0; // flaga podłączonych przęseł końcowych: -1=puste wskaźniki, 0=coś zostało, - // 1=wykonano łączenie - for (int i = 0; i < iConnection; ++i) - if (nEnds[i]) // załatwione będziemy zerować - { // każdy przebieg to próba podłączenia końca segmentu naprężania do innego zasilanego - // przęsła - if (nEnds[i]->hvTraction->hvNext[0]) - { // jeśli końcowy ma ciąg dalszy od strony 0 (Point1), szukamy odcinka najbliższego - // do Point2 - if (TractionNearestFind(nEnds[i]->hvTraction->pPoint2, 0, - nEnds[i])) // poszukiwanie przęsła - { - nEnds[i] = NULL; - zg = 1; // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo - // dotrze - } - } - else if (nEnds[i]->hvTraction->hvNext[1]) - { // jeśli końcowy ma ciąg dalszy od strony 1 (Point2), szukamy odcinka najbliższego - // do Point1 - if (TractionNearestFind(nEnds[i]->hvTraction->pPoint1, 1, - nEnds[i])) // poszukiwanie przęsła - { - nEnds[i] = NULL; - zg = 1; // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo - // dotrze - } - } - else - { // gdy koniec jest samotny, to na razie nie zostanie podłączony (nie powinno - // takich być) - nEnds[i] = NULL; + if( traction->hvParallel == nullptr ) { + ErrorLog( "Missed overhead: " + traction->asParallel ); // logowanie braku } } + } + if( traction->iTries > 0 ) { + // jeśli zaznaczony do podłączenia + // wypełnianie tabeli końców w celu szukania im połączeń + ends.emplace_back( traction ); + } } - delete[] nEnds; // nie potrzebne już -*/ + + bool connected; // nieefektywny przebieg kończy łączenie + do { + // ustalenie zastępczej rezystancji dla każdego przęsła + // flaga podłączonych przęseł końcowych: -1=puste wskaźniki, 0=coś zostało, 1=wykonano łączenie + connected = false; + for( auto &end : ends ) { + // załatwione będziemy zerować + if( end == nullptr ) { continue; } + // każdy przebieg to próba podłączenia końca segmentu naprężania do innego zasilanego przęsła + if( end->hvNext[ 0 ] != nullptr ) { + // jeśli końcowy ma ciąg dalszy od strony 0 (Point1), szukamy odcinka najbliższego do Point2 + std::tie( matchingtraction, connection ) = simulation::Region->find_traction( end->pPoint2, end, 0 ); + if( matchingtraction != nullptr ) { + // jak znalezione przęsło z zasilaniem, to podłączenie "równoległe" + end->ResistanceCalc( 0, matchingtraction->fResistance[ connection ], matchingtraction->psPower[ connection ] ); + // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo dotrze + connected = true; + end = nullptr; + } + } + else if( end->hvNext[ 1 ] != nullptr ) { + // jeśli końcowy ma ciąg dalszy od strony 1 (Point2), szukamy odcinka najbliższego do Point1 + std::tie( matchingtraction, connection ) = simulation::Region->find_traction( end->pPoint1, end, 1 ); + if( matchingtraction != nullptr ) { + // jak znalezione przęsło z zasilaniem, to podłączenie "równoległe" + end->ResistanceCalc( 1, matchingtraction->fResistance[ connection ], matchingtraction->psPower[ connection ] ); + // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo dotrze + connected = true; + end = nullptr; + } + } + else { + // gdy koniec jest samotny, to na razie nie zostanie podłączony (nie powinno takich być) + end = nullptr; + } + } + } while( true == connected ); } diff --git a/Traction.h b/Traction.h index 67f46d6a..fab4909d 100644 --- a/Traction.h +++ b/Traction.h @@ -55,13 +55,16 @@ class TTraction : public editor::basic_node { TTraction( scene::node_data const &Nodedata ); // legacy constructor TTraction( std::string Name ); - virtual ~TTraction() = default; void Load( cParser *parser, glm::dvec3 const &pOrigin ); // set origin point void origin( glm::dvec3 Origin ) { m_origin = Origin; } + // retrieves list of the track's end points + std::vector + endpoints() const; + // creates geometry data in specified geometry bank. returns: number of created elements, or NULL // NOTE: deleting nodes doesn't currently release geometry data owned by the node. TODO: implement erasing individual geometry chunks and banks #ifdef EU07_USE_OLD_GROUNDCODE diff --git a/TractionPower.cpp b/TractionPower.cpp index ade5cc5b..0b6ee2c9 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -20,14 +20,12 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- -TTractionPowerSource::TTractionPowerSource(TGroundNode const *node) : - gMyNode( node ) -{ - psNode[0] = nullptr; // sekcje zostaną podłączone do zasilaczy - psNode[1] = nullptr; -}; +TTractionPowerSource::TTractionPowerSource( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} +// legacy constructor +TTractionPowerSource::TTractionPowerSource( std::string Name ) { -TTractionPowerSource::~TTractionPowerSource(){}; + m_name = Name; +} void TTractionPowerSource::Init(double const u, double const i) { // ustawianie zasilacza przy braku w scenerii @@ -36,61 +34,67 @@ void TTractionPowerSource::Init(double const u, double const i) MaxOutputCurrent = i; }; -bool TTractionPowerSource::Load(cParser *parser) -{ - std::string token; - // AnsiString str; - // str= Parser->GetNextSymbol()LowerCase(); - // asName= str; - parser->getTokens(5); - *parser >> NominalVoltage >> VoltageFrequency >> InternalRes >> MaxOutputCurrent >> - FastFuseTimeOut; - parser->getTokens(); - *parser >> FastFuseRepetition; - parser->getTokens(); - *parser >> SlowFuseTimeOut; - parser->getTokens(); - *parser >> token; - if (token.compare("recuperation") == 0) - Recuperation = true; - else if (token.compare("section") == 0) // odłącznik sekcyjny - bSection = true; // nie jest źródłem zasilania, a jedynie informuje o prądzie odłączenia - // sekcji z obwodu - parser->getTokens(); - *parser >> token; - if (token.compare("end") != 0) - Error("tractionpowersource end statement missing"); - // if (!bSection) //odłącznik sekcji zasadniczo nie ma impedancji (0.01 jest OK) - if (InternalRes < 0.1) // coś mała ta rezystancja była... - InternalRes = 0.2; // tak około 0.2, wg - // http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf - return true; -}; +bool TTractionPowerSource::Load(cParser *parser) { -bool TTractionPowerSource::Render() -{ +#ifdef EU07_USE_OLD_GROUNDCODE + parser->getTokens( 7, false ); + *parser +#else + parser->getTokens( 10, false ); + *parser + >> m_location.x + >> m_location.y + >> m_location.z +#endif + >> NominalVoltage + >> VoltageFrequency + >> InternalRes + >> MaxOutputCurrent + >> FastFuseTimeOut + >> FastFuseRepetition + >> SlowFuseTimeOut; + + std::string token { parser->getToken() }; + if( token == "recuperation" ) { + Recuperation = true; + } + else if( token == "section" ) { + // odłącznik sekcyjny + // nie jest źródłem zasilania, a jedynie informuje o prądzie odłączenia sekcji z obwodu + bSection = true; + } + // skip rest of the section + while( ( false == token.empty() ) + && ( token != "end" ) ) { + + token = parser->getToken(); + } + + if( InternalRes < 0.1 ) { + // coś mała ta rezystancja była... + // tak około 0.2, wg + // http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf + InternalRes = 0.2; + } return true; }; bool TTractionPowerSource::Update(double dt) { // powinno być wykonane raz na krok fizyki - // if (NominalVoltage * TotalPreviousAdmitance > - // MaxOutputCurrent * 0.00000005) // iloczyn napięcia i admitancji daje prąd - // ErrorLog("Power overload: \"" + gMyNode->asName + "\" with current " + AnsiString(NominalVoltage * TotalPreviousAdmitance) + "A"); - if (NominalVoltage * TotalPreviousAdmitance > - MaxOutputCurrent) // iloczyn napięcia i admitancji daje prąd - { + // iloczyn napięcia i admitancji daje prąd + if (NominalVoltage * TotalPreviousAdmitance > MaxOutputCurrent) { + FastFuse = true; FuseCounter += 1; - if (FuseCounter > FastFuseRepetition) - { + if (FuseCounter > FastFuseRepetition) { + SlowFuse = true; - ErrorLog("Power overload: \"" + gMyNode->asName + "\" disabled for " + - std::to_string(SlowFuseTimeOut) + "s"); + ErrorLog( "Power overload: \"" + m_name + "\" disabled for " + std::to_string( SlowFuseTimeOut ) + "s" ); } - else - ErrorLog("Power overload: \"" + gMyNode->asName + "\" disabled for " + - std::to_string(FastFuseTimeOut) + "s"); + else { + ErrorLog( "Power overload: \"" + m_name + "\" disabled for " + std::to_string( FastFuseTimeOut ) + "s" ); + } + FuseTimer = 0; } if (FastFuse || SlowFuse) @@ -145,4 +149,15 @@ void TTractionPowerSource::PowerSet(TTractionPowerSource *ps) // else ErrorLog("nie może być więcej punktów zasilania niż dwa"); }; + + +// legacy method, calculates changes in simulation state over specified time +void +powergridsource_table::update( double const Deltatime ) { + + for( auto *powersource : m_items ) { + powersource->Update( Deltatime ); + } +} + //--------------------------------------------------------------------------- diff --git a/TractionPower.h b/TractionPower.h index d85330f7..ae9bb18e 100644 --- a/TractionPower.h +++ b/TractionPower.h @@ -7,14 +7,14 @@ obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef TractionPowerH -#define TractionPowerH +#pragma once + +#include "scenenode.h" #include "parser.h" //Tolaris-010603 +#include "names.h" -class TGroundNode; +class TTractionPowerSource : public editor::basic_node { -class TTractionPowerSource -{ private: double NominalVoltage = 0.0; double VoltageFrequency = 0.0; @@ -33,27 +33,34 @@ class TTractionPowerSource bool SlowFuse = false; double FuseTimer = 0.0; int FuseCounter = 0; - TGroundNode const *gMyNode = nullptr; // wskaźnik na węzeł rodzica - protected: - public: // zmienne publiczne - TTractionPowerSource *psNode[2]; // zasilanie na końcach dla sekcji +public: + // zmienne publiczne + TTractionPowerSource *psNode[ 2 ] = { nullptr, nullptr }; // zasilanie na końcach dla sekcji bool bSection = false; // czy jest sekcją - public: - // AnsiString asName; - TTractionPowerSource(TGroundNode const *node); - ~TTractionPowerSource(); + + TTractionPowerSource( scene::node_data const &Nodedata ); + // legacy constructor + TTractionPowerSource( std::string Name ); + void Init(double const u, double const i); bool Load(cParser *parser); - bool Render(); bool Update(double dt); double CurrentGet(double res); - void VoltageSet(double const v) - { - NominalVoltage = v; - }; + void VoltageSet(double const v) { + NominalVoltage = v; }; void PowerSet(TTractionPowerSource *ps); }; + + +// collection of generators for power grid present in the scene +class powergridsource_table : public basic_table { + +public: + // legacy method, calculates changes in simulation state over specified time + void + update( double const Deltatime ); +}; + //--------------------------------------------------------------------------- -#endif diff --git a/Train.cpp b/Train.cpp index 4a33dd9d..3d1c34db 100644 --- a/Train.cpp +++ b/Train.cpp @@ -357,14 +357,6 @@ TTrain::TTrain() { fPress[ i ][ j ] = 0.0; } -TTrain::~TTrain() -{ - if (DynamicObject) - if (DynamicObject->Mechanik) - DynamicObject->Mechanik->TakeControl( - true); // likwidacja kabiny wymaga przejęcia przez AI -} - bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) { // powiązanie ręcznego sterowania kabiną z pojazdem // Global::pUserDynamic=NewDynamicObject; //pojazd renderowany bez trzęsienia @@ -3587,7 +3579,7 @@ bool TTrain::Update( double const Deltatime ) iDoorNo[i] = p->iAnimType[ANIM_DOORS]; iUnits[i] = iUnitNo; cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length()]; - asCarName[i] = p->GetName(); + asCarName[i] = p->name(); bPants[iUnitNo - 1][0] = (bPants[iUnitNo - 1][0] || p->MoverParameters->PantFrontUp); bPants[iUnitNo - 1][1] = (bPants[iUnitNo - 1][1] || p->MoverParameters->PantRearUp); bComp[iUnitNo - 1][0] = (bComp[iUnitNo - 1][0] || p->MoverParameters->CompressorAllow); @@ -6259,7 +6251,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) } else if (token == "pyscreen:") { - pyScreens.init(*parser, DynamicObject->mdKabina, DynamicObject->GetName(), + pyScreens.init(*parser, DynamicObject->mdKabina, DynamicObject->name(), NewCabNo); } // btLampkaUnknown.Init("unknown",mdKabina,false); diff --git a/Train.h b/Train.h index 9fe73f93..6b077597 100644 --- a/Train.h +++ b/Train.h @@ -80,7 +80,6 @@ class TTrain bool ShowNextCurrent; // pokaz przd w podlaczonej lokomotywie (ET41) bool InitializeCab(int NewCabNo, std::string const &asFileName); TTrain(); - ~TTrain(); // McZapkie-010302 bool Init(TDynamicObject *NewDynamicObject, bool e3d = false); void OnKeyDown(int cKey); diff --git a/World.cpp b/World.cpp index 53c0634a..be3f6b32 100644 --- a/World.cpp +++ b/World.cpp @@ -207,6 +207,13 @@ void TWorld::TrainDelete(TDynamicObject *d) if (Train) if (Train->Dynamic() != d) return; // nie tego usuwać +#ifdef EU07_SCENERY_EDITOR + if( ( Train->DynamicObject ) + && ( Train->DynamicObject->Mechanik ) ) { + // likwidacja kabiny wymaga przejęcia przez AI + Train->DynamicObject->Mechanik->TakeControl( true ); + } +#endif delete Train; // i nie ma czym sterować Train = NULL; Controlled = NULL; // tego też już nie ma @@ -263,13 +270,25 @@ bool TWorld::Init( GLFWwindow *Window ) { UILayer.set_progress( "Preparing train / Przygotowanie kabiny" ); WriteLog( "Player train init: " + Global::asHumanCtrlVehicle ); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *nPlayerTrain = NULL; - if (Global::asHumanCtrlVehicle != "ghostview") - nPlayerTrain = Ground.DynamicFind(Global::asHumanCtrlVehicle); // szukanie w tych z obsadą +#else + TDynamicObject *nPlayerTrain; +#endif + if( Global::asHumanCtrlVehicle != "ghostview" ) +#ifdef EU07_USE_OLD_GROUNDCODE + nPlayerTrain = Ground.DynamicFind( Global::asHumanCtrlVehicle ); // szukanie w tych z obsadą +#else + nPlayerTrain = simulation::Vehicles.find( Global::asHumanCtrlVehicle ); +#endif if (nPlayerTrain) { Train = new TTrain(); +#ifdef EU07_USE_OLD_GROUNDCODE if (Train->Init(nPlayerTrain->DynamicObject)) +#else + if( Train->Init( nPlayerTrain ) ) +#endif { Controlled = Train->Dynamic(); mvControlled = Controlled->ControlledFind()->MoverParameters; @@ -522,7 +541,11 @@ void TWorld::OnKeyDown(int cKey) break; } +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *tmp = Ground.DynamicNearest( Camera.Pos, 50, true ); //łapiemy z obsadą +#else + TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true ) ); +#endif if( ( tmp != nullptr ) && ( tmp != Controlled ) ) { @@ -700,7 +723,11 @@ void TWorld::OnKeyDown(int cKey) */ if (cKey == Global::Keys[k_Heating]) // Ra: klawisz nie jest najszczęśliwszy { // zmiana próżny/ładowny; Ra: zabrane z kabiny - TDynamicObject *temp = Global::DynamicNearest(); +#ifdef EU07_USE_OLD_GROUNDCODE + TDynamicObject *temp = Global::DynamicNearest(); +#else + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); +#endif if (temp) { if (Global::shiftState ? temp->MoverParameters->IncBrakeMult() : @@ -743,7 +770,11 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_IncLocalBrakeLevel]) { // zahamowanie dowolnego pojazdu +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); +#else + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); +#endif if (temp) { if (Global::ctrlState) @@ -762,7 +793,11 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_DecLocalBrakeLevel]) { // odhamowanie dowolnego pojazdu +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); +#else + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); +#endif if (temp) { if (Global::ctrlState) @@ -915,20 +950,7 @@ void TWorld::FollowView(bool wycisz) { DistantView(); }; -bool TWorld::Update() -{ -#ifdef USE_SCENERY_MOVING - vector3 tmpvector = Global::GetCameraPosition(); - tmpvector = vector3(-int(tmpvector.x) + int(tmpvector.x) % 10000, - -int(tmpvector.y) + int(tmpvector.y) % 10000, - -int(tmpvector.z) + int(tmpvector.z) % 10000); - if (tmpvector.x || tmpvector.y || tmpvector.z) - { - WriteLog("Moving scenery"); - Ground.MoveGroundNode(tmpvector); - WriteLog("Scenery moved"); - }; -#endif +bool TWorld::Update() { Timer::UpdateTimers(Global::iPause != 0); @@ -955,7 +977,7 @@ bool TWorld::Update() /* fTimeBuffer += dt; //[s] dodanie czasu od poprzedniej ramki */ - m_primaryupdateaccumulator += dt; +// m_primaryupdateaccumulator += dt; // unused for the time being m_secondaryupdateaccumulator += dt; /* if (fTimeBuffer >= fMaxDt) // jest co najmniej jeden krok; normalnie 0.01s @@ -999,21 +1021,26 @@ bool TWorld::Update() dt = dt / iterations; // Ra: fizykę lepiej by było przeliczać ze stałym krokiem */ } + auto const stepdeltatime { dt / updatecount }; // NOTE: updates are limited to 20, but dt is distributed over potentially many more iterations // this means at count > 20 simulation and render are going to desync. is that right? // NOTE: experimentally changing this to prevent the desync. // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps +#ifdef EU07_USE_OLD_GROUNDCODE if( true == Global::FullPhysics ) { - // default calculation mode, each step calculated separately - for( int updateidx = 0; updateidx < updatecount; ++updateidx ) { - Ground.Update( dt / updatecount, 1 ); + // mixed calculation mode, steps calculated in ~0.05s chunks + while( updatecount >= 5 ) { + Ground.Update( stepdeltatime, 5 ); + updatecount -= 5; + } + if( updatecount ) { + Ground.Update( stepdeltatime, updatecount ); } } else { - // slightly simplified calculation mode; can lead to errors - Ground.Update( dt / updatecount, updatecount ); + // simplified calculation mode; faster but can lead to errors + Ground.Update( stepdeltatime, updatecount ); } - // yB dodał przyspieszacz fizyki if( (true == DebugModeFlag) && (true == Global::bActive) // nie przyspieszać, gdy jedzie w tle :) @@ -1024,8 +1051,24 @@ bool TWorld::Update() Ground.Update( dt, 1 ); Ground.Update( dt, 1 ); // 5 razy } - // secondary fixed step simulation time routines +#else + if( true == Global::FullPhysics ) { + // mixed calculation mode, steps calculated in ~0.05s chunks + while( updatecount >= 5 ) { + simulation::State.update( stepdeltatime, 5 ); + updatecount -= 5; + } + if( updatecount ) { + simulation::State.update( stepdeltatime, updatecount ); + } + } + else { + // simplified calculation mode; faster but can lead to errors + simulation::State.update( stepdeltatime, updatecount ); + } +#endif + // secondary fixed step simulation time routines while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) { Global::tranTexts.Update(); // obiekt obsługujący stenogramy dźwięków na ekranie @@ -1065,11 +1108,12 @@ bool TWorld::Update() #ifdef EU07_USE_OLD_GROUNDCODE Ground.CheckQuery(); + Ground.Update_Hidden(); #else simulation::Events.CheckQuery(); + simulation::Region->update(); #endif - Ground.Update_Hidden(); simulation::Lights.update(); // render time routines follow: @@ -1119,10 +1163,15 @@ TWorld::Update_Camera( double const Deltatime ) { Camera.LookAt = Controlled->GetPosition(); } else { - TDynamicObject *d = - Ground.DynamicNearest( Camera.Pos, 300 ); // szukaj w promieniu 300m +#ifdef EU07_USE_OLD_GROUNDCODE + TDynamicObject *d = Ground.DynamicNearest( Camera.Pos, 300 ); // szukaj w promieniu 300m if( !d ) d = Ground.DynamicNearest( Camera.Pos, 1000 ); // dalej szukanie, jesli bliżej nie ma +#else + TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false ) ); + if( !d ) + d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false ) ); // dalej szukanie, jesli bliżej nie ma +#endif if( d && pDynamicNearest ) { // jeśli jakiś jest znaleziony wcześniej if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { @@ -1305,7 +1354,11 @@ TWorld::Update_UI() { // timetable TDynamicObject *tmp = ( FreeFlyModeFlag ? +#ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : +#else + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : +#endif Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp == nullptr ) { break; } @@ -1386,8 +1439,12 @@ TWorld::Update_UI() { TDynamicObject *tmp = ( FreeFlyModeFlag ? - Ground.DynamicNearest( Camera.Pos ) : - Controlled ); // w trybie latania lokalizujemy wg mapy +#ifdef EU07_USE_OLD_GROUNDCODE + Ground.DynamicNearest( Camera.Pos ) : +#else + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : +#endif + Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp != nullptr ) { // @@ -1403,7 +1460,7 @@ TWorld::Update_UI() { uitextline1 += "; C0:" + ( tmp->PrevConnected ? - tmp->PrevConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( + tmp->PrevConnected->name() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ? " (" + to_string( tmp->MoverParameters->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : "" ) : @@ -1411,7 +1468,7 @@ TWorld::Update_UI() { uitextline1 += " C1:" + ( tmp->NextConnected ? - tmp->NextConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( + tmp->NextConnected->name() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ? " (" + to_string( tmp->MoverParameters->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : "" ) : @@ -1653,7 +1710,11 @@ TWorld::Update_UI() { TDynamicObject *tmp = ( FreeFlyModeFlag ? +#ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : +#else + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : +#endif Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp == nullptr ) { break; @@ -1867,6 +1928,7 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 11 + i, (unsigned)(pRozkaz->cString[10 + i])) + " rcvd"); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *t = Ground.DynamicFind( std::string(pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[10 + i])); // nazwa pojazdu jest druga @@ -1878,6 +1940,19 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) NULL, stopExt); // floaty są z przodu WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); } +#else + // nazwa pojazdu jest druga + auto *vehicle = simulation::Vehicles.find( { pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[ 10 + i ] } ); + if( ( vehicle != nullptr ) + && ( vehicle->Mechanik != nullptr ) ) { + vehicle->Mechanik->PutCommand( + { pRozkaz->cString + 9, static_cast(i) }, + pRozkaz->fPar[0], pRozkaz->fPar[1], + nullptr, + stopExt ); // floaty są z przodu + WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); + } +#endif } break; case 4: // badanie zajętości toru @@ -1920,6 +1995,7 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) " rcvd"); if (pRozkaz->cString[0]) // jeśli długość nazwy jest niezerowa { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7 +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *t; if (pRozkaz->cString[1] == '*') t = Ground.DynamicFind( @@ -1929,6 +2005,9 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) pRozkaz->cString + 1, (unsigned)pRozkaz->cString[0])); // nazwa pojazdu if (t) Ground.WyslijNamiary(t); // wysłanie informacji o pojeździe +#else + // TODO: implement +#endif } else { // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, @@ -1964,34 +2043,34 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); - if (pRozkaz->cString[1]) // jeśli długość nazwy jest niezerowa - { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 - TGroundNode *t; - if (pRozkaz->cString[2] == '*') - t = Ground.DynamicFind( - Global::asHumanCtrlVehicle); // nazwa pojazdu użytkownika - else - t = Ground.DynamicFindAny( - std::string(pRozkaz->cString + 2, - (unsigned)pRozkaz->cString[1])); // nazwa pojazdu - if (t) - { - TDynamicObject *d = t->DynamicObject; - while (d) - { - d->Damage(pRozkaz->cString[0]); - d = d->Next(); // pozostałe też - } - d = t->DynamicObject->Prev(); - while (d) - { - d->Damage(pRozkaz->cString[0]); - d = d->Prev(); // w drugą stronę też - } - Ground.WyslijUszkodzenia(t->asName, t->DynamicObject->MoverParameters->EngDmgFlag); // zwrot informacji o pojeździe - } - } - // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); +#ifdef EU07_USE_OLD_GROUNDCODE + if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa + { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 + TGroundNode *t; + if( pRozkaz->cString[ 2 ] == '*' ) + t = Ground.DynamicFind( + Global::asHumanCtrlVehicle ); // nazwa pojazdu użytkownika + else + t = Ground.DynamicFindAny( + std::string( pRozkaz->cString + 2, + (unsigned)pRozkaz->cString[ 1 ] ) ); // nazwa pojazdu + if( t ) { + TDynamicObject *d = t->DynamicObject; + while( d ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Next(); // pozostałe też + } + d = t->DynamicObject->Prev(); + while( d ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Prev(); // w drugą stronę też + } + Ground.WyslijUszkodzenia( t->asName, t->DynamicObject->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe + } + } +#else + // TODO: implement +#endif break; } }; @@ -2143,7 +2222,7 @@ void TWorld::ChangeDynamic() { Train->DynamicSet( temp ); Controlled = temp; mvControlled = Controlled->ControlledFind()->MoverParameters; - Global::asHumanCtrlVehicle = Train->Dynamic()->GetName(); + Global::asHumanCtrlVehicle = Train->Dynamic()->name(); if( Train->Dynamic()->Mechanik ) // AI może sobie samo pójść if( !Train->Dynamic()->Mechanik->AIControllFlag ) // tylko jeśli ręcznie prowadzony { diff --git a/openglgeometrybank.h b/openglgeometrybank.h index ee0aa234..05a813c1 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -25,8 +25,8 @@ struct basic_vertex { glm::vec2 texture; // uv space basic_vertex() = default; - basic_vertex( glm::vec3 const &Position, glm::vec3 const &Normal, glm::vec2 const &Texture ) : - position( Position ), normal( Normal ), texture( Texture ) + basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) : + position( Position ), normal( Normal ), texture( Texture ) {} void serialize( std::ostream& ) const; void deserialize( std::istream& ); @@ -58,8 +58,8 @@ struct geometry_handle { geometry_handle() : bank( 0 ), chunk( 0 ) {} - geometry_handle( std::uint32_t const Bank, std::uint32_t const Chunk ) : - bank( Bank ), chunk( Chunk ) + geometry_handle( std::uint32_t Bank, std::uint32_t Chunk ) : + bank( Bank ), chunk( Chunk ) {} // methods inline @@ -121,8 +121,8 @@ protected: unsigned int type; // kind of geometry used by the chunk vertex_array vertices; // geometry data // NOTE: constructor doesn't copy provided vertex data, but moves it - geometry_chunk( vertex_array &Vertices, unsigned int const Type ) : - type( Type ) + geometry_chunk( vertex_array &Vertices, unsigned int Type ) : + type( Type ) { vertices.swap( Vertices ); } diff --git a/parser.cpp b/parser.cpp index 465b1d7b..c220b0c2 100644 --- a/parser.cpp +++ b/parser.cpp @@ -205,23 +205,21 @@ std::string cParser::readToken(bool ToLower, const char *Break) if (token.compare("include") == 0) { // obsługa include std::string includefile = readToken(ToLower); // nazwa pliku - if (LoadTraction ? true : ((includefile.find("tr/") == std::string::npos) && - (includefile.find("tra/") == std::string::npos))) - { - // std::string trtest2="niemaproblema"; //nazwa odporna na znalezienie "tr/" - // if (trtest=="x") //jeśli nie wczytywać drutów - // trtest2=includefile; //kopiowanie ścieżki do pliku + if( ( true == LoadTraction ) + || ( ( includefile.find( "tr/" ) == std::string::npos ) + && ( includefile.find( "tra/" ) == std::string::npos ) ) ) { + std::string parameter = readToken(false); // w parametrach nie zmniejszamy while( (parameter.empty() == false) && (parameter.compare("end") != 0) ) { - parameters.push_back(parameter); + parameters.emplace_back(parameter); parameter = readToken(false); } // if (trtest2.find("tr/")!=0) mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction); if (mIncludeParser->mSize <= 0) - ErrorLog("Missed include: " + includefile); + ErrorLog("Bad include: can't open file \"" + includefile + "\"" ); } else { while( token.compare( "end" ) != 0 ) { diff --git a/renderer.cpp b/renderer.cpp index 7c1ac70d..3107a28e 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1602,7 +1602,17 @@ opengl_renderer::Render( scene::basic_region *Region ) { m_sectionqueue.clear(); m_cellqueue.clear(); - +/* + for( auto *section : Region->sections( m_renderpass.camera.position(), m_renderpass.draw_range * Global::fDistanceFactor ) ) { +#ifdef EU07_USE_DEBUG_CULLING + if( m_worldcamera.camera.visible( section->m_area ) ) { +#else + if( m_renderpass.camera.visible( section->m_area ) ) { +#endif + m_sectionqueue.emplace_back( section ); + } + } +*/ // build a list of region sections to render glm::vec3 const cameraposition { m_renderpass.camera.position() }; auto const camerax = static_cast( std::floor( cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) ); @@ -1903,11 +1913,22 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator switch( m_renderpass.draw_mode ) { case rendermode::color: - case rendermode::shadows: - case rendermode::pickscenery: { - // render + case rendermode::shadows: { // opaque parts of instanced models for( auto *instance : cell->m_instancesopaque ) { Render( instance ); } + // opaque parts of vehicles + for( auto *path : cell->m_paths ) { + for( auto *dynamic : path->Dynamics ) { + Render( dynamic ); + } + } + } + case rendermode::pickscenery: { + // opaque parts of instanced models + for( auto *instance : cell->m_instancesopaque ) { + ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); + Render( instance ); + } // TODO: add remaining content types break; } @@ -2863,7 +2884,12 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque // translucent parts of instanced models for( auto *instance : cell->m_instancetranslucent ) { Render_Alpha( instance ); } - // TODO: add remaining content types + // translucent parts of vehicles + for( auto *path : cell->m_paths ) { + for( auto *dynamic : path->Dynamics ) { + Render_Alpha( dynamic ); + } + } ++first; } diff --git a/scene.cpp b/scene.cpp index 43adb766..2dfb70da 100644 --- a/scene.cpp +++ b/scene.cpp @@ -10,11 +10,28 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "scene.h" +#include "globals.h" #include "renderer.h" #include "logs.h" 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(); + } +*/ + // TBD, TODO: move to sound renderer + for( auto *path : m_paths ) { + // dźwięki pojazdów, również niewidocznych + path->RenderDynSounds(); + } +} + // adds provided shape to the cell void basic_cell::insert( shape_node Shape ) { @@ -90,6 +107,131 @@ basic_cell::insert( TAnimModel *Instance ) { } } +// registers provided path in the lookup directory of the cell +void +basic_cell::register_end( TTrack *Path ) { + + m_directories.paths.emplace( Path ); +} + +// registers provided traction piece in the lookup directory of the cell +void +basic_cell::register_end( TTraction *Traction ) { + + m_directories.traction.emplace( 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_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { + + TDynamicObject *vehiclenearest { nullptr }; + float leastdistance { std::numeric_limits::max() }; + float distance; + float const distancecutoff { Radius * Radius }; // we'll ignore vehicles farther than this + + for( auto *path : m_paths ) { + for( auto *vehicle : path->Dynamics ) { + if( ( true == Onlycontrolled ) + && ( vehicle->Mechanik == nullptr ) ) { + continue; + } + distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point ); + if( ( distance > distancecutoff ) + || ( distance > leastdistance ) ){ + continue; + } + std::tie( vehiclenearest, leastdistance ) = std::tie( vehicle, distance ); + } + } + return std::tie( vehiclenearest, leastdistance ); +} + +// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint +std::tuple +basic_cell::find( glm::dvec3 const &Point, TTrack const *Exclude ) { + + Math3D::vector3 point { Point.x, Point.y, Point.z }; // sad workaround until math classes unification + int endpointid; + + for( auto *path : m_directories.paths ) { + + if( path == Exclude ) { continue; } + + endpointid = path->TestPoint( &point ); + if( endpointid >= 0 ) { + + return std::tie( path, endpointid ); + } + } + return { nullptr, -1 }; +} + +// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint +std::tuple +basic_cell::find( glm::dvec3 const &Point, TTraction const *Exclude ) { + + int endpointid; + + for( auto *traction : m_directories.traction ) { + + if( traction == Exclude ) { continue; } + + endpointid = traction->TestPoint( Point ); + if( endpointid >= 0 ) { + + return std::tie( traction, endpointid ); + } + } + return { nullptr, -1 }; +} + +// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece +std::tuple +basic_cell::find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) { + + TTraction + *tractionnearest { nullptr }; + float + distance, + distancenearest { std::numeric_limits::max() }; + int endpoint, + endpointnearest { -1 }; + + for( auto *traction : m_directories.traction ) { + + if( ( traction == Other ) + || ( traction->psSection != Other->psSection ) + || ( traction == Other->hvNext[ 0 ] ) + || ( traction == Other->hvNext[ 1 ] ) ) { + // ignore pieces from different sections, and ones connected to the other piece + continue; + } + endpoint = ( + glm::dot( traction->vParametric, Other->vParametric ) >= 0.0 ? + Currentdirection ^ 1 : + Currentdirection ); + if( ( traction->psPower[ endpoint ] == nullptr ) + || ( traction->fResistance[ endpoint ] < 0.0 ) ) { + continue; + } + distance = glm::length2( traction->location() - Point ); + if( distance < distancenearest ) { + std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( traction, endpoint, distance ); + } + } + return { tractionnearest, endpointnearest, distancenearest }; +} + +// sets center point of the section +void +basic_cell::center( glm::dvec3 Center ) { + + m_area.center = Center; + // NOTE: we should also update origin point for the contained nodes, but in practice we can skip this + // as all nodes will be added only after the proper center point was set, and won't change +} + // generates renderable version of held non-instanced geometry void basic_cell::create_geometry( geometrybank_handle const &Bank ) { @@ -104,17 +246,23 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { #endif } -// sets center point of the section + + +// legacy method, updates sounds and polls event launchers within radius around specified point void -basic_cell::center( glm::dvec3 Center ) { +basic_section::update( glm::dvec3 const &Location, float const Radius ) { - m_area.center = Center; - // NOTE: we should also update origin point for the contained nodes, but in practice we can skip this - // as all nodes will be added only after the proper center point was set, and won't change + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) }; + + for( auto &cell : m_cells ) { + + if( glm::length2( cell.area().center - Location ) < squaredradii ) { + // we reject cells which aren't within our area of interest + cell.update(); + } + } } - - // adds provided shape to the section void basic_section::insert( shape_node Shape ) { @@ -168,6 +316,96 @@ basic_section::insert( TAnimModel *Instance ) { 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 ) { + + // go through sections within radius of interest, and pick the nearest candidate + TDynamicObject + *vehiclefound, + *vehiclenearest { nullptr }; + float + distancefound, + distancenearest { std::numeric_limits::max() }; + + 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 - Point ) > squaredradii ) { + continue; + } + std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled ); + if( ( vehiclefound != nullptr ) + && ( distancefound < distancenearest ) ) { + + std::tie( vehiclenearest, distancenearest ) = std::tie( vehiclefound, distancefound ); + } + } + return std::tie( vehiclenearest, distancenearest ); +} + +// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint +std::tuple +basic_section::find( glm::dvec3 const &Point, TTrack const *Exclude ) { + + return cell( Point ).find( Point, Exclude ); +} + +// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint +std::tuple +basic_section::find( glm::dvec3 const &Point, TTraction const *Exclude ) { + + return cell( Point ).find( Point, Exclude ); +} + +// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece +std::tuple +basic_section::find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) { + + // go through sections within radius of interest, and pick the nearest candidate + TTraction + *tractionfound, + *tractionnearest { nullptr }; + float + distancefound, + distancenearest { std::numeric_limits::max() }; + int + endpointfound, + endpointnearest { -1 }; + + 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 - Point ) > squaredradii ) { + continue; + } + std::tie( tractionfound, endpointfound, distancefound ) = cell.find( Point, Other, Currentdirection ); + if( ( tractionfound != nullptr ) + && ( distancefound < distancenearest ) ) { + + std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( tractionfound, endpointfound, distancefound ); + } + } + return { tractionnearest, endpointnearest, distancenearest }; +} + // sets center point of the section void basic_section::center( glm::dvec3 Center ) { @@ -251,6 +489,17 @@ basic_region::~basic_region() { for( auto section : m_sections ) { if( section != nullptr ) { delete section; } } } +// legacy method, updates sounds and polls event launchers around camera +void +basic_region::update() { + // render events and sounds from sectors near enough to the viewer + auto const range = 2750.f; // audible range of 100 db sound + auto const §ionlist = sections( Global::pCameraPosition, range ); + for( auto *section : sectionlist ) { + section->update( Global::pCameraPosition, range ); + } +} + void basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { @@ -327,6 +576,11 @@ basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { // 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 ) + ")" ); } + // also register path ends in appropriate sections, for path merging lookups + // TODO: clean this up during track refactoring + for( auto &point : Path->endpoints() ) { + register_path( Path, point ); + } } // inserts provided track in the region @@ -344,6 +598,11 @@ basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) { // 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 ) + ")" ); } + // also register traction ends in appropriate sections, for path merging lookups + // TODO: clean this up during track refactoring + for( auto &point : Traction->endpoints() ) { + register_traction( Traction, point ); + } } // inserts provided instance of 3d model in the region @@ -363,6 +622,154 @@ basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) } } +// inserts provided sound in the region +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(); + + if( point_inside( center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( center ).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 ) + ")" ); + } +*/ +} + +// find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers +std::tuple +basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { + + auto const §ionlist = sections( Point, Radius ); + // go through sections within radius of interest, and pick the nearest candidate + TDynamicObject + *foundvehicle, + *nearestvehicle { nullptr }; + float + founddistance, + nearestdistance { std::numeric_limits::max() }; + + for( auto *section : sectionlist ) { + std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled ); + if( ( foundvehicle != nullptr ) + && ( founddistance < nearestdistance ) ) { + + std::tie( nearestvehicle, nearestdistance ) = std::tie( foundvehicle, founddistance ); + } + } + return std::tie( nearestvehicle, nearestdistance ); +} + +// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint +std::tuple +basic_region::find_path( glm::dvec3 const &Point, TTrack const *Exclude ) { + + // TBD: throw out of bounds exception instead of checks all over the place..? + if( point_inside( Point ) ) { + + return section( Point ).find( Point, Exclude ); + } + + return std::make_tuple( nullptr, -1 ); +} + +// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint +std::tuple +basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Exclude ) { + + // TBD: throw out of bounds exception instead of checks all over the place..? + if( point_inside( Point ) ) { + + return section( Point ).find( Point, Exclude ); + } + + return std::make_tuple( nullptr, -1 ); +} + +// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece +std::tuple +basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) { + + auto const §ionlist = sections( Point, 0.f ); + // go through sections within radius of interest, and pick the nearest candidate + TTraction + *tractionfound, + *tractionnearest { nullptr }; + float + distancefound, + distancenearest { std::numeric_limits::max() }; + int + endpointfound, + endpointnearest { -1 }; + + for( auto *section : sectionlist ) { + std::tie( tractionfound, endpointfound, distancefound ) = section->find( Point, Other, Currentdirection ); + if( ( tractionfound != nullptr ) + && ( distancefound < distancenearest ) ) { + + std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( tractionfound, endpointfound, distancefound ); + } + } + return { tractionnearest, endpointnearest }; +} + +// finds sections inside specified sphere. returns: list of sections +std::vector const & +basic_region::sections( glm::dvec3 const &Point, float const Radius ) { + + m_scratchpad.sections.clear(); + + auto const centerx { static_cast( std::floor( Point.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; + auto const centerz { static_cast( std::floor( Point.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; + auto const sectioncount { 2 * static_cast( std::ceil( Radius / EU07_SECTIONSIZE ) ) }; + + int const originx = centerx - sectioncount / 2; + int const originz = centerz - sectioncount / 2; + + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) + Radius, 2 ) }; + + for( int row = originz; row <= originz + sectioncount; ++row ) { + if( row < 0 ) { continue; } + if( row >= EU07_REGIONSIDESECTIONCOUNT ) { break; } + for( int column = originx; column <= originx + sectioncount; ++column ) { + if( column < 0 ) { continue; } + if( column >= EU07_REGIONSIDESECTIONCOUNT ) { break; } + + auto *section { m_sections[ row * EU07_REGIONSIDESECTIONCOUNT + column ] }; + if( ( section != nullptr ) + && ( glm::length2( section->area().center - Point ) < squaredradii ) ) { + + m_scratchpad.sections.emplace_back( section ); + } + } + } + return m_scratchpad.sections; +} + +// registers specified path in the lookup directory of a cell enclosing specified point +void +basic_region::register_path( TTrack *Path, glm::dvec3 const &Point ) { + + if( point_inside( Point ) ) { + section( Point ).register_end( Path, Point ); + } +} + +// registers specified end point of the provided traction piece in the lookup directory of the region +void +basic_region::register_traction( TTraction *Traction, glm::dvec3 const &Point ) { + + if( point_inside( Point ) ) { + section( Point ).register_end( Traction, Point ); + } +} + // checks whether specified point is within boundaries of the region bool basic_region::point_inside( glm::dvec3 const &Location ) { @@ -566,8 +973,8 @@ basic_region::RaTriangleDivider( shape_node &Shape, std::deque &Shap basic_section & basic_region::section( glm::dvec3 const &Location ) { - auto const column = static_cast( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); - auto const row = static_cast( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); + auto const column { static_cast( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; + auto const row { static_cast( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; auto §ion = m_sections[ diff --git a/scene.h b/scene.h index 66314e13..1ccd42ba 100644 --- a/scene.h +++ b/scene.h @@ -30,6 +30,18 @@ struct scratch_data { std::stack location_offset; glm::vec3 location_rotation; + + struct trainset_data { + + std::string name; + std::string track; + float offset { 0.f }; + float velocity { 0.f }; + std::vector vehicles; + std::vector couplings; + TDynamicObject * driver { nullptr }; + bool is_open { false }; + } trainset; }; // basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision @@ -40,6 +52,9 @@ class basic_cell { public: // methods + // legacy method, updates sounds and polls event launchers within radius around specified point + void + update(); // adds provided shape to the cell void insert( shape_node Shape ); @@ -52,18 +67,42 @@ public: // adds provided model instance to the cell void insert( TAnimModel *Instance ); + // registers provided path in the lookup directory of the cell + void + register_end( TTrack *Path ); + // registers provided traction piece in the lookup directory of the cell + void + register_end( TTraction *Traction ); + // 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 ); + // finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint + std::tuple + find( glm::dvec3 const &Point, TTrack const *Exclude ); + // finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint + std::tuple + find( glm::dvec3 const &Point, TTraction const *Exclude ); + // finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece + std::tuple + find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ); + // sets center point of the cell + void + center( glm::dvec3 Center ); // generates renderable version of held non-instanced geometry in specified geometry bank void create_geometry( geometrybank_handle const &Bank ); - // sets center point of the cell - void - center( glm::dvec3 Center ); + // provides access to bounding area data + bounding_area const & + area() const { + return m_area; } private: // types using shapenode_sequence = std::vector; using path_sequence = std::vector; + using path_set = std::set; using traction_sequence = std::vector; + using traction_set = std::set; using instance_sequence = std::vector; // members scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) }; @@ -75,6 +114,11 @@ private: instance_sequence m_instancesopaque; instance_sequence m_instancetranslucent; traction_sequence m_traction; + // search helpers + struct lookup_data { + path_set paths; + traction_set traction; + } m_directories; }; // basic scene partitioning structure, holds terrain geometry and collection of cells @@ -84,6 +128,9 @@ class basic_section { public: // methods + // legacy method, updates sounds and polls event launchers within radius around specified point + void + update( glm::dvec3 const &Location, float const Radius ); // adds provided shape to the section void insert( shape_node Shape ); @@ -96,12 +143,34 @@ public: // adds provided model instance to the section void insert( TAnimModel *Instance ); - // generates renderable version of held non-instanced geometry + // registers specified end point of the provided path in the lookup directory of the region void - create_geometry(); + 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 ); + // 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 ); + // finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint + std::tuple + find( glm::dvec3 const &Point, TTrack const *Exclude ); + // finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint + std::tuple + find( glm::dvec3 const &Point, TTraction const *Exclude ); + // finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece + std::tuple + find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ); // sets center point of the section void center( glm::dvec3 Center ); + // generates renderable version of held non-instanced geometry + void + create_geometry(); + // provides access to bounding area data + bounding_area const & + area() const { + return m_area; } private: // types @@ -134,6 +203,9 @@ public: // destructor ~basic_region(); // methods + // legacy method, updates sounds and polls event launchers around camera + void + update(); // inserts provided shape in the region void insert_shape( shape_node Shape, scratch_data &Scratchpad ); @@ -146,17 +218,45 @@ public: // inserts provided instance of 3d model in the region void insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ); - + // inserts provided sound in the region + void + insert_sound( TTextSound *Sound, 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 ); + // finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint + std::tuple + find_path( glm::dvec3 const &Point, TTrack const *Exclude ); + // finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint + std::tuple + find_traction( glm::dvec3 const &Point, TTraction const *Exclude ); + // finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece + std::tuple + find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ); + // finds sections inside specified sphere. returns: list of sections + std::vector const & + sections( glm::dvec3 const &Point, float const Radius ); private: // types using section_array = std::array; + struct region_scratchpad { + + std::vector sections; + }; + // methods + // registers specified end point of the provided path in the lookup directory of the region + void + register_path( TTrack *Path, glm::dvec3 const &Point ); + // registers specified end point of the provided traction piece in the lookup directory of the region + void + register_traction( TTraction *Traction, glm::dvec3 const &Point ); // checks whether specified point is within boundaries of the region bool point_inside( glm::dvec3 const &Location ); - // trims provided shape to fit into a section, adds trimmed part at the end of provided list + // legacy method, trims provided shape to fit into a section. adds trimmed part at the end of provided list, returns true if changes were made bool RaTriangleDivider( shape_node &Shape, std::deque &Shapes ); // provides access to section enclosing specified point @@ -165,6 +265,7 @@ private: // members section_array m_sections; + region_scratchpad m_scratchpad; }; diff --git a/scenenode.cpp b/scenenode.cpp index 015bdd92..1be3d643 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -17,7 +17,7 @@ namespace scene { // restores content of the node from provded input stream shape_node & -shape_node::deserialize( cParser &Input, node_data const &Nodedata ) { +shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { // import common data m_name = Nodedata.name; @@ -291,6 +291,5 @@ basic_node::basic_node( scene::node_data const &Nodedata ) : std::numeric_limits::max() ); } - } // editor //--------------------------------------------------------------------------- diff --git a/scenenode.h b/scenenode.h index fed77544..ea4e029f 100644 --- a/scenenode.h +++ b/scenenode.h @@ -72,7 +72,7 @@ class node_manager { struct node_data { double range_min { 0.0 }; - double range_max { 0.0 }; + double range_max { std::numeric_limits::max() }; std::string name; std::string type; }; @@ -86,7 +86,7 @@ public: // types struct shapenode_data { // placement and visibility - bounding_area area; // bounding area, in world coordinates + scene::bounding_area area; // bounding area, in world coordinates bool visible { true }; // visibility flag double rangesquared_min { 0.0 }; // visibility range, min double rangesquared_max { 0.0 }; // visibility range, max @@ -103,7 +103,7 @@ public: // methods // restores content of the node from provded input stream shape_node & - deserialize( cParser &Input, node_data const &Nodedata ); + deserialize( cParser &Input, scene::node_data const &Nodedata ); // adds content of provided node to already enclosed geometry. returns: true if merge could be performed bool merge( shape_node &Shape ); @@ -229,12 +229,15 @@ public: std::string const & name() const { return m_name; } - glm::dvec3 const & - location() { - return m_location; }; void location( glm::dvec3 const Location ) { m_location = Location; } + glm::dvec3 const & + location() const { + return m_location; }; + void + visible( bool const Visible ) { + m_visible = Visible; } bool visible() const { return m_visible; } diff --git a/simulation.cpp b/simulation.cpp index 9fdc5174..843d23b1 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -12,15 +12,19 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "logs.h" +#include "uilayer.h" namespace simulation { state_manager State; event_manager Events; -memory_manager Memory; +memory_table Memory; path_table Paths; traction_table Traction; -instance_manager Instances; +powergridsource_table Powergrid; +sound_table Sounds; +instance_table Instances; +vehicle_table Vehicles; light_array Lights; scene::basic_region *Region { nullptr }; @@ -44,6 +48,23 @@ state_manager::deserialize( std::string const &Scenariofile ) { return true; } +// legacy method, calculates changes in simulation state over specified time +void +state_manager::update( double const Deltatime, int Iterationcount ) { + // aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń + if (Deltatime == 0.0) { + // jeśli załączona jest pauza, to tylko obsłużyć ruch w kabinie trzeba + return; + } + + auto const totaltime { Deltatime * Iterationcount }; + // NOTE: we perform animations first, as they can determine factors like contact with powergrid + TAnimModel::AnimUpdate( totaltime ); // wykonanie zakolejkowanych animacji + + simulation::Powergrid.update( totaltime ); + simulation::Vehicles.update( Deltatime, Iterationcount ); +} + // restores class data from provided stream void state_manager::deserialize( cParser &Input ) { @@ -79,7 +100,11 @@ state_manager::deserialize( cParser &Input ) { for( auto &function : functionlist ) { functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( importscratchpad ) ) ); } + // deserialize content from the provided input + auto + timelast { std::chrono::steady_clock::now() }, + timenow { timelast }; std::string token { Input.getToken() }; while( false == token.empty() ) { @@ -91,6 +116,14 @@ state_manager::deserialize( cParser &Input ) { ErrorLog( "Bad scenario: unexpected token \"" + token + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); } + timenow = std::chrono::steady_clock::now(); + if( std::chrono::duration_cast( timenow - timelast ).count() >= 200 ) { + timelast = timenow; + glfwPollEvents(); + UILayer.set_progress( Input.getProgress(), Input.getFullProgress() ); + GfxRenderer.Render(); + } + token = Input.getToken(); } } @@ -218,6 +251,7 @@ state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scrat simulation::Paths.InitTracks(); simulation::Traction.InitTraction(); simulation::Events.InitEvents(); + simulation::Memory.InitCells(); } void @@ -230,6 +264,8 @@ state_manager::deserialize_light( cParser &Input, scene::scratch_data &Scratchpa void state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) { + auto const inputline = Input.Line(); // cache in case we need to report error + scene::node_data nodedata; // common data and node type indicator Input.getTokens( 4 ); @@ -240,13 +276,27 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad >> nodedata.type; // type-based deserialization. not elegant but it'll do if( nodedata.type == "dynamic" ) { - // TODO: implement - skip_until( Input, "enddynamic" ); + + auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) }; + // vehicle import can potentially fail + if( vehicle == nullptr ) { return; } + + if( false == simulation::Vehicles.insert( vehicle ) ) { + + ErrorLog( "Bad scenario: vehicle with duplicate name, \"" + vehicle->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + + if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only + && ( ( vehicle->MoverParameters->SecuritySystem.SystemType != 0 ) + || ( vehicle->MoverParameters->SandCapacity > 0.0 ) ) ) { + // we check for presence of security system or sand load, as a way to determine whether the vehicle is a controllable engine + // NOTE: this isn't 100% precise, e.g. middle EZT module comes with security system, while it has no lights, and some engines + // don't have security systems fitted + simulation::Lights.insert( vehicle ); + } } else if( nodedata.type == "track" ) { - auto const inputline = Input.Line(); // cache in case we need to report error - auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; // duplicates of named tracks are currently experimentally allowed if( simulation::Paths.insert( path ) ) { @@ -262,10 +312,10 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } else if( nodedata.type == "traction" ) { - auto const inputline = Input.Line(); // cache in case we need to report error - auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) }; - // duplicates of named tracks are currently discarded + // traction loading is optional + if( traction == nullptr ) { return; } + if( simulation::Traction.insert( traction ) ) { simulation::Region->insert_traction( traction, Scratchpad ); } @@ -274,18 +324,29 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } } else if( nodedata.type == "tractionpowersource" ) { - // TODO: implement - skip_until( Input, "end" ); + + auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) }; + // traction loading is optional + if( powersource == nullptr ) { return; } + + if( simulation::Powergrid.insert( powersource ) ) { +/* + // TODO: implement this + simulation::Region.insert_powersource( powersource, Scratchpad ); +*/ + } + else { + 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" ) { if( nodedata.range_min < 0.0 ) { // convert and import 3d terrain + // TODO: implement this } else { // regular instance of 3d mesh - auto const inputline = Input.Line(); // cache in case we need to report error - auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; // model import can potentially fail if( instance == nullptr ) { return; } @@ -313,10 +374,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } else if( nodedata.type == "memcell" ) { - auto const inputline = Input.Line(); // cache in case we need to report error - auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) }; - // duplicates of named tracks are currently discarded if( simulation::Memory.insert( memorycell ) ) { /* // TODO: implement this @@ -325,10 +383,6 @@ 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 ) + ")" ); -/* - delete memorycell; - delete memorycellnode; -*/ } } else if( nodedata.type == "eventlauncher" ) { @@ -336,8 +390,14 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad skip_until( Input, "end" ); } else if( nodedata.type == "sound" ) { - // TODO: implement - skip_until( Input, "endsound" ); + + auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) }; + if( simulation::Sounds.insert( sound ) ) { + 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 ) + ")" ); + } } } @@ -417,14 +477,68 @@ state_manager::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad void state_manager::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) { - // TODO: implement - skip_until( Input, "endtrainset" ); + if( true == Scratchpad.trainset.is_open ) { + // shouldn't happen but if it does wrap up currently open trainset and report an error + deserialize_endtrainset( Input, Scratchpad ); + ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + to_string( Input.Line() ) + ")" ); + } + + Scratchpad.trainset = scene::scratch_data::trainset_data(); + Scratchpad.trainset.is_open = true; + + Input.getTokens( 4 ); + Input + >> Scratchpad.trainset.name + >> Scratchpad.trainset.track + >> Scratchpad.trainset.offset + >> Scratchpad.trainset.velocity; } void state_manager::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) { - // TODO: implement + if( ( false == Scratchpad.trainset.is_open ) + || ( true == Scratchpad.trainset.vehicles.empty() ) ) { + // not bloody likely but we better check for it just the same + ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + to_string( Input.Line() - 1 ) + ")" ); + Scratchpad.trainset.is_open = false; + return; + } + + std::size_t vehicleindex { 0 }; + for( auto *vehicle : Scratchpad.trainset.vehicles ) { + // go through list of vehicles in the trainset, coupling them together and checking for potential driver + if( ( vehicle->Mechanik != nullptr ) + && ( vehicle->Mechanik->Primary() ) ) { + // primary driver will receive the timetable for this trainset + Scratchpad.trainset.driver = vehicle; + } + if( vehicleindex > 0 ) { + // from second vehicle on couple it with the previous one + Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev( + vehicle, + Scratchpad.trainset.couplings[ vehicleindex - 1 ] ); + } + ++vehicleindex; + } + + if( Scratchpad.trainset.driver != nullptr ) { + // if present, send timetable to the driver + // wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy + auto *controller = Scratchpad.trainset.driver->Mechanik; + controller->DirectionInitial(); + controller->PutCommand( + "Timetable:" + Scratchpad.trainset.name, + Scratchpad.trainset.velocity, + 0, + nullptr ); + } + if( Scratchpad.trainset.couplings.back() == coupling::faux ) { + // jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi) + Scratchpad.trainset.vehicles.back()->RaLightsSet( -1, TMoverParameters::light::rearendsignals ); + } + // all done + Scratchpad.trainset.is_open = false; } // creates path and its wrapper, restoring class data from provided stream @@ -448,6 +562,10 @@ state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad TTraction * state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + if( false == Global::bLoadTraction ) { + skip_until( Input, "endtraction" ); + return nullptr; + } // TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done auto *traction = new TTraction( Nodedata ); auto offset = ( @@ -459,6 +577,22 @@ state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratc return traction; } +TTractionPowerSource * +state_manager::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + if( false == Global::bLoadTraction ) { + skip_until( Input, "end" ); + return nullptr; + } + + auto *powersource = new TTractionPowerSource( Nodedata ); + powersource->Load( &Input ); + // adjust location + powersource->location( transform( powersource->location(), Scratchpad ) ); + + return powersource; +} + TMemCell * state_manager::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { @@ -481,7 +615,6 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa >> location.y >> location.z >> rotation.y; - // adjust location auto *instance = new TAnimModel( Nodedata ); instance->RaAnglesSet( Scratchpad.location_rotation + rotation ); // dostosowanie do pochylania linii @@ -495,6 +628,147 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa return instance; } +TDynamicObject * +state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + if( false == Scratchpad.trainset.is_open ) { + // part of trainset data is used when loading standalone vehicles, so clear it just in case + Scratchpad.trainset = scene::scratch_data::trainset_data(); + } + auto const inputline { Input.Line() }; // cache in case of errors + // basic attributes + auto const datafolder { Input.getToken() }; + auto const skinfile { Input.getToken() }; + auto const mmdfile { Input.getToken() }; + auto const pathname = ( + Scratchpad.trainset.is_open ? + Scratchpad.trainset.track : + Input.getToken() ); + auto const offset { Input.getToken( false ) }; + auto const drivertype { Input.getToken() }; + auto const couplingparams = ( + Scratchpad.trainset.is_open ? + Input.getToken() : + "3" ); + auto const velocity = ( + Scratchpad.trainset.is_open ? + Scratchpad.trainset.velocity : + Input.getToken( false ) ); + // extract coupling type and optional parameters + auto const couplingparamsplit = couplingparams.find( '.' ); + auto coupling = ( + couplingparamsplit != std::string::npos ? + std::atoi( couplingparams.substr( 0, couplingparamsplit ).c_str() ) : + std::atoi( couplingparams.c_str() ) ); + if( coupling < 0 ) { + // sprzęg zablokowany (pojazdy nierozłączalne przy manewrach) + coupling = ( -coupling ) | coupling::permanent; + } + if( ( offset != -1.0 ) + && ( std::abs( offset ) > 0.5 ) ) { // maksymalna odległość między sprzęgami - do przemyślenia + // likwidacja sprzęgu, jeśli odległość zbyt duża - to powinno być uwzględniane w fizyce sprzęgów... + coupling = 0; + } + auto const params = ( + couplingparamsplit != std::string::npos ? + couplingparams.substr( couplingparamsplit + 1 ) : + "" ); + // load amount and type + auto loadcount { Input.getToken( false ) }; + auto loadtype = ( + loadcount ? + Input.getToken() : + "" ); + if( loadtype == "enddynamic" ) { + // idiotoodporność: ładunek bez podanego typu nie liczy się jako ładunek + loadcount = 0; + loadtype = ""; + } + + auto *path = simulation::Paths.find( pathname ); + if( path == nullptr ) { + + ErrorLog( "Bad scenario: vehicle \"" + Nodedata.name + "\" placed on nonexistent path \"" + pathname + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + skip_until( Input, "enddynamic" ); + return nullptr; + } + + if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd, + && ( false == path->asEvent0Name.empty() ) // tor ma Event0 + && ( std::abs( velocity ) <= 1.f ) // a skład stoi + && ( Scratchpad.trainset.offset >= 0.0 ) // ale może nie sięgać na owy tor + && ( Scratchpad.trainset.offset < 8.0 ) ) { // i raczej nie sięga + // przesuwamy około pół EU07 dla wstecznej zgodności + Scratchpad.trainset.offset = 8.0; + } + + auto *vehicle = new TDynamicObject(); + + auto const length = + vehicle->Init( + Nodedata.name, + datafolder, skinfile, mmdfile, + path, + ( offset == -1.0 ? + Scratchpad.trainset.offset : + Scratchpad.trainset.offset - offset ), + drivertype, + velocity, + Scratchpad.trainset.name, + loadcount, loadtype, + ( offset == -1.0 ), + params ); + + if( length != 0.0 ) { // zero oznacza błąd + // przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1 + Scratchpad.trainset.offset -= length; + // automatically establish permanent connections for couplers which specify them in their definitions + if( ( coupling != 0 ) + && ( vehicle->MoverParameters->Couplers[ ( offset == -1.0 ? 0 : 1 ) ].AllowedFlag & coupling::permanent ) ) { + coupling |= coupling::permanent; + } + if( true == Scratchpad.trainset.is_open ) { + Scratchpad.trainset.vehicles.emplace_back( vehicle ); + Scratchpad.trainset.couplings.emplace_back( coupling ); + } + } + else { + delete vehicle; + skip_until( Input, "enddynamic" ); + return nullptr; + } + + auto const destination { Input.getToken() }; + if( destination != "enddynamic" ) { + // optional vehicle destination parameter + vehicle->asDestination = Input.getToken(); + skip_until( Input, "enddynamic" ); + } + + return vehicle; +} + +TTextSound * +state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) { + + glm::dvec3 location; + Input.getTokens( 3 ); + Input + >> location.x + >> location.y + >> location.z; + // adjust location + location = transform( location, Scratchpad ); + + auto const soundname { Input.getToken() }; + auto *sound = new TTextSound( soundname, Nodedata.range_max, location.x, location.y, location.z, false, false, Nodedata.range_min ); + sound->name( Nodedata.name ); + + skip_until( Input, "endsound" ); + + return sound; +} + // skips content of stream until specified token void state_manager::skip_until( cParser &Input, std::string const &Token ) { diff --git a/simulation.h b/simulation.h index 3aa6bbf0..781785d4 100644 --- a/simulation.h +++ b/simulation.h @@ -9,14 +9,18 @@ http://mozilla.org/MPL/2.0/. #pragma once +#include "parser.h" +#include "scene.h" #include "event.h" #include "memcell.h" #include "track.h" #include "traction.h" +#include "tractionpower.h" +#include "realsound.h" #include "animmodel.h" -#include "scene.h" +#include "dynobj.h" +#include "driver.h" #include "lightarray.h" -#include "parser.h" namespace simulation { @@ -26,6 +30,9 @@ public: // types // methods + // legacy method, calculates changes in simulation state over specified time + void + update( double dt, int iter ); bool deserialize( std::string const &Scenariofile ); @@ -51,8 +58,11 @@ private: void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ); TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); 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 ); // skips content of stream until specified token void skip_until( cParser &Input, std::string const &Token ); // transforms provided location by specifed rotation and offset @@ -61,10 +71,13 @@ private: extern state_manager State; extern event_manager Events; -extern memory_manager Memory; +extern memory_table Memory; extern path_table Paths; extern traction_table Traction; -extern instance_manager Instances; +extern powergridsource_table Powergrid; +extern sound_table Sounds; +extern instance_table Instances; +extern vehicle_table Vehicles; extern light_array Lights; extern scene::basic_region *Region;