diff --git a/AirCoupler.cpp b/AirCoupler.cpp index 62b72624..5c6c0b09 100644 --- a/AirCoupler.cpp +++ b/AirCoupler.cpp @@ -42,9 +42,9 @@ void TAirCoupler::Init(std::string const &asName, TModel3d *pModel) { // wyszukanie submodeli if (!pModel) return; // nie ma w czym szukać - pModelOn = pModel->GetFromName( (asName + "_on").c_str() ); // połączony na wprost - pModelOff = pModel->GetFromName( (asName + "_off").c_str() ); // odwieszony - pModelxOn = pModel->GetFromName( (asName + "_xon").c_str() ); // połączony na skos + pModelOn = pModel->GetFromName( asName + "_on" ); // połączony na wprost + pModelOff = pModel->GetFromName( asName + "_off" ); // odwieszony + pModelxOn = pModel->GetFromName( asName + "_xon" ); // połączony na skos } void TAirCoupler::Load(cParser *Parser, TModel3d *pModel) diff --git a/AnimModel.cpp b/AnimModel.cpp index 02e33d9c..b956331a 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -205,8 +205,9 @@ void TAnimContainer::AnimSetVMD(double fNewSpeed) // "+AnsiString(pMovementData->f3Vector.y)+" "+AnsiString(pMovementData->f3Vector.z)); } -void TAnimContainer::UpdateModel() -{ // przeliczanie animacji wykonać tylko raz na model +// przeliczanie animacji wykonać tylko raz na model +void TAnimContainer::UpdateModel() { + if (pSubModel) // pozbyć się tego - sprawdzać wcześniej { if (fTranslateSpeed != 0.0) @@ -236,7 +237,7 @@ void TAnimContainer::UpdateModel() // zakończeniu } } - if (fRotateSpeed != 0) + if (fRotateSpeed != 0.0) { /* @@ -303,8 +304,9 @@ void TAnimContainer::UpdateModel() // zakończeniu } } - if (fAngleSpeed != 0.0) - { // obrót kwaternionu (interpolacja) + if( fAngleSpeed != 0.f ) { + // NOTE: this is angle- not quaternion-based rotation TBD, TODO: switch to quaternion rotations? + fAngleCurrent += fAngleSpeed * Timer::GetDeltaTime(); // aktualny parametr interpolacji } } }; @@ -322,15 +324,14 @@ void TAnimContainer::PrepareModel() { if (fAngleSpeed > 0.0f) { - fAngleCurrent += - fAngleSpeed * Timer::GetDeltaTime(); // aktualny parametr interpolacji if (fAngleCurrent >= 1.0f) { // interpolacja zakończona, ustawienie na pozycję końcową qCurrent = qDesired; fAngleSpeed = 0.0; // wyłączenie przeliczania wektora - if (evDone) - Global::AddToQuery(evDone, - NULL); // wykonanie eventu informującego o zakończeniu + if( evDone ) { + // wykonanie eventu informującego o zakończeniu + Global::AddToQuery( evDone, NULL ); + } } else { // obliczanie pozycji pośredniej @@ -412,9 +413,9 @@ TAnimModel::TAnimModel() iNumLights = 0; fBlinkTimer = 0; - for (int i = 0; i < iMaxNumLights; i++) + for (int i = 0; i < iMaxNumLights; ++i) { - LightsOn[i] = LightsOff[i] = NULL; // normalnie nie ma + LightsOn[i] = LightsOff[i] = nullptr; // normalnie nie ma lsLights[i] = ls_Off; // a jeśli są, to wyłączone } vAngle.x = vAngle.y = vAngle.z = 0.0; // zerowanie obrotów egzemplarza @@ -440,14 +441,15 @@ bool TAnimModel::Init(TModel3d *pNewModel) bool TAnimModel::Init(std::string const &asName, std::string const &asReplacableTexture) { - if (asReplacableTexture.substr(0, 1) == - "*") // od gwiazdki zaczynają się teksty na wyświetlaczach - asText = asReplacableTexture.substr(1, asReplacableTexture.length() - 1); // zapamiętanie tekstu - else if (asReplacableTexture != "none") - m_materialdata.replacable_skins[1] = - GfxRenderer.Fetch_Texture( asReplacableTexture, "" ); - if( ( m_materialdata.replacable_skins[ 1 ] != 0 ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) ) { + if( asReplacableTexture.substr( 0, 1 ) == "*" ) { + // od gwiazdki zaczynają się teksty na wyświetlaczach + asText = asReplacableTexture.substr( 1, asReplacableTexture.length() - 1 ); // zapamiętanie tekstu + } + else if( asReplacableTexture != "none" ) { + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( asReplacableTexture ); + } + if( ( m_materialdata.replacable_skins[ 1 ] != null_handle ) + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) ) { // tekstura z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha = 0x31310031; } @@ -521,11 +523,11 @@ bool TAnimModel::Load(cParser *parser, bool ter) return true; } -TAnimContainer * TAnimModel::AddContainer(char *pName) +TAnimContainer * TAnimModel::AddContainer(std::string const &Name) { // dodanie sterowania submodelem dla egzemplarza if (!pModel) return NULL; - TSubModel *tsb = pModel->GetFromName(pName); + TSubModel *tsb = pModel->GetFromName(Name); if (tsb) { TAnimContainer *tmp = new TAnimContainer(); @@ -537,20 +539,27 @@ TAnimContainer * TAnimModel::AddContainer(char *pName) return NULL; } -TAnimContainer * TAnimModel::GetContainer(char *pName) +TAnimContainer * TAnimModel::GetContainer(std::string const &Name) { // szukanie/dodanie sterowania submodelem dla egzemplarza - if (!pName) + if (true == Name.empty()) return pRoot; // pobranie pierwszego (dla obrotnicy) TAnimContainer *pCurrent; for (pCurrent = pRoot; pCurrent != NULL; pCurrent = pCurrent->pNext) // if (pCurrent->GetName()==pName) - if (std::string(pName) == pCurrent->NameGet()) + if (Name == pCurrent->NameGet()) return pCurrent; - return AddContainer(pName); + return AddContainer(Name); } -void TAnimModel::RaAnimate() -{ // przeliczenie animacji - jednorazowo na klatkę +// przeliczenie animacji - jednorazowo na klatkę +void TAnimModel::RaAnimate( unsigned int const Framestamp ) { + + if( Framestamp == m_framestamp ) { return; } + + fBlinkTimer -= Timer::GetDeltaTime(); + if( fBlinkTimer <= 0 ) + fBlinkTimer += fOffTime; + // Ra 2F1I: to by można pomijać dla modeli bez animacji, których jest większość TAnimContainer *pCurrent; for (pCurrent = pRoot; pCurrent != NULL; pCurrent = pCurrent->pNext) @@ -559,15 +568,14 @@ void TAnimModel::RaAnimate() // if () //tylko dla modeli z IK !!!! for (pCurrent = pRoot; pCurrent != NULL; pCurrent = pCurrent->pNext) // albo osobny łańcuch pCurrent->UpdateModelIK(); // przeliczenie odwrotnej kinematyki + + m_framestamp = Framestamp; }; void TAnimModel::RaPrepare() { // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza - fBlinkTimer -= Timer::GetDeltaTime(); - if (fBlinkTimer <= 0) - fBlinkTimer = fOffTime; bool state; // stan światła - for (int i = 0; i < iNumLights; i++) + for (int i = 0; i < iNumLights; ++i) { switch (lsLights[i]) { @@ -605,23 +613,8 @@ int TAnimModel::Flags() return i; }; -//----------------------------------------------------------------------------- -// 2011-03-16 funkcje renderowania z możliwością pochylania obiektów -//----------------------------------------------------------------------------- -#ifdef EU07_USE_OLD_RENDERCODE -void TAnimModel::Render( vector3 const &Position ) { - RaAnimate(); // jednorazowe przeliczenie animacji - RaPrepare(); - if( pModel ) // renderowanie rekurencyjne submodeli - GfxRenderer.Render( pModel, Material(), Position, vAngle ); -}; -void TAnimModel::RenderAlpha( vector3 const &Position ) { - RaPrepare(); - if( pModel ) // renderowanie rekurencyjne submodeli - GfxRenderer.Render_Alpha( pModel, Material(), Position, vAngle ); -}; -#endif //--------------------------------------------------------------------------- + bool TAnimModel::TerrainLoaded() { // zliczanie kwadratów kilometrowych (główna linia po Next) do tworznia tablicy return (this ? pModel != NULL : false); diff --git a/AnimModel.h b/AnimModel.h index 73bad8f7..e24ab335 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "Model3d.h" -#include "Texture.h" +#include "material.h" #include "DynObj.h" const int iMaxNumLights = 8; @@ -149,8 +149,9 @@ class TAnimModel { TLightState lsLights[iMaxNumLights]; float fDark; // poziom zapalanie światła (powinno być chyba powiązane z danym światłem?) float fOnTime, fOffTime; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza - private: - void RaAnimate(); // przeliczenie animacji egzemplarza + unsigned int m_framestamp { 0 }; // id of last rendered gfx frame +private: + void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji egzemplarza void RaPrepare(); // ustawienie animacji egzemplarza na wzorcu public: static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania @@ -162,12 +163,8 @@ class TAnimModel { bool Init(TModel3d *pNewModel); bool Init(std::string const &asName, std::string const &asReplacableTexture); bool Load(cParser *parser, bool ter = false); - TAnimContainer * AddContainer(char *pName); - TAnimContainer * GetContainer(char *pName); -#ifdef EU07_USE_OLD_RENDERCODE - void Render( vector3 const &Position ); - void RenderAlpha( vector3 const &Position ); -#endif + TAnimContainer * AddContainer(std::string const &Name); + TAnimContainer * GetContainer(std::string const &Name = ""); int Flags(); void RaAnglesSet(double a, double b, double c) { @@ -178,9 +175,6 @@ class TAnimModel { bool TerrainLoaded(); int TerrainCount(); TSubModel * TerrainSquare(int n); -#ifdef EU07_USE_OLD_RENDERCODE - void TerrainRenderVBO(int n); -#endif void AnimationVND(void *pData, double a, double b, double c, double d); void LightSet(int n, float v); static void AnimUpdate(double dt); diff --git a/Button.cpp b/Button.cpp index 06211b1d..8629da2b 100644 --- a/Button.cpp +++ b/Button.cpp @@ -28,8 +28,8 @@ void TButton::Init(std::string const &asName, TModel3d *pModel, bool bNewOn) { if( pModel == nullptr ) { return; } - pModelOn = pModel->GetFromName( (asName + "_on").c_str() ); - pModelOff = pModel->GetFromName( (asName + "_off").c_str() ); + pModelOn = pModel->GetFromName( asName + "_on" ); + pModelOff = pModel->GetFromName( asName + "_off" ); m_state = bNewOn; Update(); }; @@ -77,7 +77,7 @@ void TButton::Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2) { bool TButton::Load_mapping( cParser &Input ) { - if( false == Input.getTokens( 2, true, ", " ) ) { + if( false == Input.getTokens( 2, true, " ,\n\r\t" ) ) { return false; } diff --git a/Console.cpp b/Console.cpp index 076d4332..5f67d082 100644 --- a/Console.cpp +++ b/Console.cpp @@ -359,7 +359,7 @@ void Console::ValueSet(int x, double y) WriteLog("CalibrateOutDebugInfo: oryginal=" + std::to_string(y), false); if (Global::fCalibrateOutMax[x] > 0) { - y = Global::CutValueToRange(0, y, Global::fCalibrateOutMax[x]); + y = clamp( y, 0.0, Global::fCalibrateOutMax[x]); if (Global::iCalibrateOutDebugInfo == x) WriteLog(" cutted=" + std::to_string(y), false); y = y / Global::fCalibrateOutMax[x]; // sprowadzenie do <0,1> jeśli podana diff --git a/Console/PoKeys55.cpp b/Console/PoKeys55.cpp index 8fbb4dbd..c27efaaf 100644 --- a/Console/PoKeys55.cpp +++ b/Console/PoKeys55.cpp @@ -156,7 +156,7 @@ bool TPoKeys55::Connect() // get the structure (after we have allocated enough memory for the structure.) DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); // First call populates "StructureSize" with the correct value - SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, + SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, InterfaceDataStructure, NULL, 0, &StructureSize, NULL); DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize)); // Allocate enough memory diff --git a/Driver.cpp b/Driver.cpp index d898603e..f96be222 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -70,6 +70,8 @@ Tutaj łączymy teorię z praktyką - tu nic nie działa i nikt nie wie dlaczego // 17. otwieranie/zamykanie drzwi // 18. Ra: odczepianie z zahamowaniem i podczepianie // 19. dla Humandriver: tasma szybkosciomierza - zapis do pliku! +// 20. wybór pozycji zaworu maszynisty w oparciu o zadane opoznienie hamowania + // do zrobienia: // 1. kierownik pociagu @@ -145,12 +147,12 @@ double GetDistanceToEvent(TTrack* track, TEvent* event, double scan_dir, double //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- -inline TSpeedPos::TSpeedPos(TTrack *track, double dist, int flag) +TSpeedPos::TSpeedPos(TTrack *track, double dist, int flag) { Set(track, dist, flag); }; -inline TSpeedPos::TSpeedPos(TEvent *event, double dist, TOrders order) +TSpeedPos::TSpeedPos(TEvent *event, double dist, TOrders order) { Set(event, dist, order); }; @@ -234,51 +236,42 @@ void TSpeedPos::CommandCheck() bool TSpeedPos::Update() { - if (fDist < 0.0) + if( fDist < 0.0 ) iFlags |= spElapsed; // trzeba zazanaczyć, że minięty - if (iFlags & spTrack) // jeśli tor + if (iFlags & spTrack) // road/track { - if (trTrack) // może być NULL, jeśli koniec toru (???) - { - fVelNext = trTrack->VelocityGet(); // aktualizacja prędkości (może być zmieniana - // eventem) - int i; - if ((i = iFlags & 0xF0000000) != 0) - { // jeśli skrzyżowanie, ograniczyć prędkość przy skręcaniu - if (abs(i) > 0x10000000) //±1 to jazda na wprost, ±2 nieby też, ale z przecięciem - // głównej drogi - chyba że jest równorzędne... - fVelNext = 30.0; // uzależnić prędkość od promienia; albo niech będzie - // ograniczona w skrzyżowaniu (velocity z ujemną wartością) - if( ( iFlags & spElapsed ) == 0 ) { - // jeśli nie wjechał - if( false == trTrack->Dynamics.empty() ) { - if( Global::iWriteLogEnabled & 8 ) { - WriteLog( "Tor " + trTrack->NameGet() + " zajety przed pojazdem. Num=" + std::to_string( trTrack->Dynamics.size() ) + "Dist= " + std::to_string( fDist ) ); - fVelNext = 0.0; // to zabronić wjazdu (chyba że ten z przodu też jedzie prosto) + if (trTrack) { + // może być NULL, jeśli koniec toru (???) + fVelNext = trTrack->VelocityGet(); // aktualizacja prędkości (może być zmieniana eventem) + + if( trTrack->iCategoryFlag & 1 ) { + // railways + if( iFlags & spSwitch ) { + // jeśli odcinek zmienny + if( ( ( trTrack->GetSwitchState() & 1 ) != 0 ) != + ( ( iFlags & spSwitchStatus ) != 0 ) ) { + // czy stan się zmienił? + // Ra: zakładam, że są tylko 2 możliwe stany + iFlags ^= spSwitchStatus; + if( ( iFlags & spElapsed ) == 0 ) { + // jeszcze trzeba skanowanie wykonać od tego toru + // problem jest chyba, jeśli zwrotnica się przełoży zaraz po zjechaniu z niej + // na Mydelniczce potrafi skanować na wprost mimo pojechania na bok + return true; } } } } - if (iFlags & spSwitch) // jeśli odcinek zmienny - { - if (((trTrack->GetSwitchState() & 1) != 0) != - ((iFlags & spSwitchStatus) != 0)) // czy stan się zmienił? - { // Ra: zakładam, że są tylko 2 możliwe stany - iFlags ^= spSwitchStatus; - // fVelNext=trTrack->VelocityGet(); //nowa prędkość - if ((iFlags & spElapsed) == 0) - return true; // jeszcze trzeba skanowanie wykonać od tego toru - // problem jest chyba, jeśli zwrotnica się przełoży zaraz po zjechaniu z niej - // na Mydelniczce potrafi skanować na wprost mimo pojechania na bok - } - // poniższe nie dotyczy trybu łączenia? - if( ( ( iFlags & spElapsed ) == 0 ) - && ( false == trTrack->Dynamics.empty() ) ) { - // jeśli jeszcze nie wjechano na tor, a coś na nim jest - if( Global::iWriteLogEnabled & 8 ) { - WriteLog( "Rozjazd " + trTrack->NameGet() + " zajety przed pojazdem. Num=" + std::to_string( trTrack->Dynamics.size() ) + "Dist= " + std::to_string( fDist ) ); + else { + // roads and others + if( iFlags & 0xF0000000 ) { + // jeśli skrzyżowanie, ograniczyć prędkość przy skręcaniu + if( ( iFlags & 0xF0000000 ) > 0x10000000 ) { + // ±1 to jazda na wprost, ±2 nieby też, ale z przecięciem głównej drogi - chyba że jest równorzędne... + // TODO: uzależnić prędkość od promienia; + // albo niech będzie ograniczona w skrzyżowaniu (velocity z ujemną wartością) + fVelNext = 30.0; } - fVelNext = 0.0; // to niech stanie w zwiększonej odległości } } } @@ -310,14 +303,8 @@ std::string TSpeedPos::TableText() { // pozycja tabelki pr?dko?ci if (iFlags & spEnabled) { // o ile pozycja istotna - return "Flags:" + to_hex_str(iFlags, 6) + ", Dist:" + to_string(fDist, 1, 6) + + return "Flags:" + to_hex_str(iFlags, 8) + ", Dist:" + to_string(fDist, 1, 6) + ", Vel:" + (fVelNext == -1.0 ? " - " : to_string(static_cast(fVelNext), 0, 3)) + ", Name:" + GetName(); - //if (iFlags & spTrack) // jeśli tor - // return "Flags=#" + IntToHex(iFlags, 8) + ", Dist=" + FloatToStrF(fDist, ffFixed, 7, 1) + - // ", Vel=" + AnsiString(fVelNext) + ", Track=" + trTrack->NameGet(); - //else if (iFlags & spEvent) // jeśli event - // return "Flags=#" + IntToHex(iFlags, 8) + ", Dist=" + FloatToStrF(fDist, ffFixed, 7, 1) + - // ", Vel=" + AnsiString(fVelNext) + ", Event=" + evEvent->asName; } return "Empty"; } @@ -370,7 +357,7 @@ void TSpeedPos::Set(TTrack *track, double dist, int flag) trTrack = track; // TODO: (t) może być NULL i nie odczytamy końca poprzedniego :/ if (trTrack) { - iFlags = flag | (trTrack->eType == tt_Normal ? 2 : 10); // zapamiętanie kierunku wraz z typem + iFlags = flag | (trTrack->eType == tt_Normal ? spTrack : (spTrack | spSwitch) ); // zapamiętanie kierunku wraz z typem if (iFlags & spSwitch) if (trTrack->GetSwitchState() & 1) iFlags |= spSwitchStatus; @@ -381,9 +368,10 @@ void TSpeedPos::Set(TTrack *track, double dist, int flag) fVelNext = (trTrack->iCategoryFlag & 1) ? 0.0 : 20.0; // jeśli koniec, to pociąg stój, a samochód zwolnij - vPos = (((iFlags & spReverse) != 0) != ((iFlags & spEnd) != 0)) ? - trTrack->CurrentSegment()->FastGetPoint_1() : - trTrack->CurrentSegment()->FastGetPoint_0(); + vPos = + ( iFlags & spReverse ) ? + trTrack->CurrentSegment()->FastGetPoint_1() : + trTrack->CurrentSegment()->FastGetPoint_0(); } }; @@ -617,10 +605,16 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if (pTrack != nullptr ) { // jeśli kolejny istnieje if( tLast != nullptr ) { + + if( ( tLast->VelocityGet() > 0 ) + && ( ( tLast->VelocityGet() < pTrack->VelocityGet() ) + || ( pTrack->VelocityGet() < 0 ) ) ) { +/* if( ( pTrack->VelocityGet() < 0 ? tLast->VelocityGet() > 0 : pTrack->VelocityGet() > tLast->VelocityGet() ) ) { // jeśli kolejny ma większą prędkość niż poprzedni, to zapamiętać poprzedni (do czasu wyjechania) + if( ( ( ( iLast != -1 ) && ( TestFlag( sSpeedTable[ iLast ].iFlags, spEnabled | spTrack ) ) ) ? ( sSpeedTable[ iLast ].trTrack != tLast ) : @@ -637,6 +631,24 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) spEnabled | spReverse ); } } +*/ + if( ( iLast != -1 ) + && ( sSpeedTable[ iLast ].trTrack == tLast ) ) { + // if the track is already in the table we only need to mark it as relevant + sSpeedTable[ iLast ].iFlags |= spEnabled; + } + else { + // otherwise add it + TableAddNew(); + sSpeedTable[ iLast ].Set( + tLast, + fCurrentDistance - fTrackLength, // by now the current distance points to beginning of next track + ( fLastDir > 0 ? + pTrack->iPrevDirection : + pTrack->iNextDirection ) ? + spEnabled : + spEnabled | spReverse ); + } } } // zwiększenie skanowanej odległości tylko jeśli istnieje dalszy tor @@ -645,8 +657,9 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) else { // definitywny koniec skanowania, chyba że dalej puszczamy samochód po gruncie... if( ( iLast == -1 ) - || ( false == TestFlag( sSpeedTable[iLast].iFlags, spEnabled | spEnd ) ) ) { - // only if we haven't already marked end of the track + || ( ( false == TestFlag( sSpeedTable[iLast].iFlags, spEnabled | spEnd ) ) + && ( sSpeedTable[iLast].trTrack != tLast ) ) ) { + // only if we haven't already marked end of the track and if the new track doesn't duplicate last one if( TableAddNew() ) { // zapisanie ostatniego sprawdzonego toru sSpeedTable[iLast].Set( @@ -656,6 +669,11 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) spEnabled | spEnd )); } } + else if( sSpeedTable[ iLast ].trTrack == tLast ) { + // otherwise just mark the last added track as the final one + // TODO: investigate exactly how we can wind up not marking the last existing track as actual end + sSpeedTable[ iLast ].iFlags |= spEnd; + } // to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica, czy jakieś transportery return; } @@ -684,7 +702,7 @@ void TController::TableCheck(double fDistance) sp.UpdateDistance(MoveDistanceGet()); // aktualizacja odległości dla wszystkich pozycji tabeli } MoveDistanceReset(); // kasowanie odległości po aktualizacji tabelki - for( int i = 0; i <= iLast; ++i ) + for( int i = 0; i < iLast; ++i ) { // aktualizacja rekordów z wyjątkiem ostatniego if (sSpeedTable[i].iFlags & spEnabled) // jeśli pozycja istotna { @@ -697,8 +715,9 @@ void TController::TableCheck(double fDistance) while (sSpeedTable.back().trTrack != sSpeedTable[i].trTrack) { // usuwamy wszystko dopóki nie trafimy na tą zwrotnicę sSpeedTable.pop_back(); - iLast--; + --iLast; } + tLast = sSpeedTable[ i ].trTrack; TableTraceRoute( fDistance, pVehicles[ 1 ] ); TableSort(); // nie kontynuujemy pętli, trzeba doskanować ciąg dalszy @@ -706,7 +725,7 @@ void TController::TableCheck(double fDistance) } if (sSpeedTable[i].iFlags & spTrack) // jeśli odcinek { - if (sSpeedTable[i].fDist < -fLength) // a skład wyjechał całą długością poza + if( sSpeedTable[ i ].fDist + sSpeedTable[ i ].trTrack->Length() < -fLength ) // a skład wyjechał całą długością poza { // degradacja pozycji sSpeedTable[i].iFlags &= ~spEnabled; // nie liczy się } @@ -1007,10 +1026,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } } // koniec obsługi W4 v = sSpeedTable[i].fVelNext; // odczyt prędkości do zmiennej pomocniczej - if (sSpeedTable[i].iFlags & - spSwitch) // zwrotnice są usuwane z tabelki dopiero po zjechaniu z nich - iDrivigFlags |= - moveSwitchFound; // rozjazd z przodu/pod ogranicza np. sens skanowania wstecz + if( sSpeedTable[ i ].iFlags & spSwitch ) { + // zwrotnice są usuwane z tabelki dopiero po zjechaniu z nich + iDrivigFlags |= moveSwitchFound; // rozjazd z przodu/pod ogranicza np. sens skanowania wstecz + } else if (sSpeedTable[i].iFlags & spEvent) // W4 może się deaktywować { // jeżeli event, może być potrzeba wysłania komendy, aby ruszył // sprawdzanie eventów pasywnych miniętych @@ -1156,10 +1175,11 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } //sprawdzenie eventów pasywnych przed nami - if ((mvOccupied->CategoryFlag & 1) ? - sSpeedTable[i].fDist > pVehicles[0]->fTrackBlock - 20.0 : - false) // jak sygnał jest dalej niż zawalidroga + if( ( mvOccupied->CategoryFlag & 1 ) + && ( sSpeedTable[ i ].fDist > pVehicles[ 0 ]->fTrackBlock - 20.0 ) ) { + // jak sygnał jest dalej niż zawalidroga v = 0.0; // to może być podany dla tamtego: jechać tak, jakby tam stop był + } else { // zawalidrogi nie ma (albo pojazd jest samochodem), sprawdzić sygnał if (sSpeedTable[i].iFlags & spShuntSemaphor) // jeśli Tm - w zasadzie to sprawdzić @@ -1227,79 +1247,89 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } } else if (sSpeedTable[i].evEvent->StopCommand()) - { // jeśli prędkość jest zerowa, a komórka zawiera komendę - eSignNext = sSpeedTable[i].evEvent; // dla informacji - if (iDrivigFlags & - moveStopHere) // jeśli ma stać, dostaje komendę od razu - go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu - else if (sSpeedTable[i].fDist <= 20.0) // jeśli ma dociągnąć, to niech - // dociąga (moveStopCloser - // dotyczy dociągania do W4, nie - // semafora) - go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu + { // jeśli prędkość jest zerowa, a komórka zawiera komendę + eSignNext = sSpeedTable[ i ].evEvent; // dla informacji + if( true == TestFlag( iDrivigFlags, moveStopHere ) ) { + // jeśli ma stać, dostaje komendę od razu + go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu + } + else if( sSpeedTable[ i ].fDist <= 20.0 ) { + // jeśli ma dociągnąć, to niech dociąga + // (moveStopCloser dotyczy dociągania do W4, nie semafora) + go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu + } } } // jeśli nie ma zawalidrogi } // jeśli event if (v >= 0.0) { // pozycje z prędkością -1 można spokojnie pomijać d = sSpeedTable[i].fDist; - if ((sSpeedTable[i].iFlags & spElapsed) ? - false : - d > 0.0) // sygnał lub ograniczenie z przodu (+32=przejechane) - { // 2014-02: jeśli stoi, a ma do przejechania kawałek, to niech jedzie - if ((mvOccupied->Vel == 0.0) ? - ((sSpeedTable[i].iFlags & - (spEnabled | spEvent | spPassengerStopPoint)) == - (spEnabled | spEvent | spPassengerStopPoint)) && - (d > fMaxProximityDist) : - false) - a = (iDrivigFlags & moveStopCloser) ? fAcc : 0.0; // ma podjechać bliżej - - // czy na pewno w tym - // miejscu taki warunek? - else - { - a = (v * v - mvOccupied->Vel * mvOccupied->Vel) / - (25.92 * d); // przyspieszenie: ujemne, gdy trzeba hamować - if (d < fMinProximityDist) // jak jest już blisko - if (v < fVelDes) - fVelDes = v; // ograniczenie aktualnej prędkości + if( ( d > 0.0 ) + && ( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) ) { + // sygnał lub ograniczenie z przodu (+32=przejechane) + // 2014-02: jeśli stoi, a ma do przejechania kawałek, to niech jedzie + if( ( mvOccupied->Vel == 0.0 ) + && ( d > fMaxProximityDist ) + && ( true == TestFlag( sSpeedTable[ i ].iFlags, ( spEnabled | spEvent | spPassengerStopPoint ) ) ) ) { + // ma podjechać bliżej - czy na pewno w tym miejscu taki warunek? + a = ( + iDrivigFlags & moveStopCloser ? + fAcc : + 0.0 ); + } + else { + // przyspieszenie: ujemne, gdy trzeba hamować + a = ( v * v - mvOccupied->Vel * mvOccupied->Vel ) / ( 25.92 * d ); + if( ( mvOccupied->Vel < v ) + || ( v == 0.0 ) ) { + // if we're going slower than the target velocity and there's enough room for safe stop, speed up + auto const brakingdistance = fBrakeDist * braking_distance_multiplier( v ); + if( brakingdistance > 0.0 ) { + // maintain desired acc while we have enough room to brake safely, when close enough start paying attention + // try to make a smooth transition instead of sharp change + a = interpolate( a, AccPreferred, clamp( ( d - brakingdistance ) / brakingdistance, 0.0, 1.0 ) ); + } + } + if( ( d < fMinProximityDist ) + && ( v < fVelDes ) ) { + // jak jest już blisko, ograniczenie aktualnej prędkości + fVelDes = v; + } } } else if (sSpeedTable[i].iFlags & spTrack) // jeśli tor { // tor ogranicza prędkość, dopóki cały skład nie przejedzie, // d=fLength+d; //zamiana na długość liczoną do przodu - if (v >= 1.0) // EU06 się zawieszało po dojechaniu na koniec toru postojowego - if (d < -fLength) + if( v >= 1.0 ) // EU06 się zawieszało po dojechaniu na koniec toru postojowego + if( d + sSpeedTable[ i ].trTrack->Length() < -fLength ) continue; // zapętlenie, jeśli już wyjechał za ten odcinek - if (v < fVelDes) - fVelDes = - v; // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie + if( v < fVelDes ) { + // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie + fVelDes = v; + } // if (v==0.0) fAcc=-0.9; //hamowanie jeśli stop continue; // i tyle wystarczy } else // event trzyma tylko jeśli VelNext=0, nawet po przejechaniu (nie powinno // dotyczyć samochodów?) a = (v == 0.0 ? -1.0 : fAcc); // ruszanie albo hamowanie - if (a < fAcc && v == Min0R(v, fNext)) - { // mniejsze przyspieszenie to mniejsza możliwość rozpędzenia się albo konieczność - // hamowania + + if ((a < fAcc) && (v == std::min(v, fNext))) { + // mniejsze przyspieszenie to mniejsza możliwość rozpędzenia się albo konieczność hamowania // jeśli droga wolna, to może być a>1.0 i się tu nie załapuje - // if (mvOccupied->Vel>10.0) fAcc = a; // zalecane przyspieszenie (nie musi być uwzględniane przez AI) fNext = v; // istotna jest prędkość na końcu tego odcinka fDist = d; // dlugość odcinka } - else if ((fAcc > 0) && (v > 0) && (v <= fNext)) - { // jeśli nie ma wskazań do hamowania, można podać drogę i prędkość na jej końcu + else if ((fAcc > 0) && (v > 0) && (v <= fNext)) { + // jeśli nie ma wskazań do hamowania, można podać drogę i prędkość na jej końcu fNext = v; // istotna jest prędkość na końcu tego odcinka - fDist = d; // dlugość odcinka (kolejne pozycje mogą wydłużać drogę, jeśli - // prędkość jest stała) + fDist = d; // dlugość odcinka (kolejne pozycje mogą wydłużać drogę, jeśli prędkość jest stała) } } // if (v>=0.0) if (fNext >= 0.0) { // jeśli ograniczenie - if ((sSpeedTable[i].iFlags & (spEnabled | spEvent)) == - (spEnabled | spEvent)) // tylko sygnał przypisujemy + if ((sSpeedTable[i].iFlags & (spEnabled | spEvent)) == (spEnabled | spEvent)) // tylko sygnał przypisujemy if (!eSignNext) // jeśli jeszcze nic nie zapisane tam eSignNext = sSpeedTable[i].evEvent; // dla informacji if (fNext == 0.0) @@ -1312,7 +1342,8 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN (OrderCurrentGet() & Obey_train)) VelSignalLast = -1.0; // jeśli mieliśmy ograniczenie z semafora i nie ma przed nami - if (VelSignalLast >= 0.0) //analiza spisanych z tabelki ograniczeń i nadpisanie aktualnego + //analiza spisanych z tabelki ograniczeń i nadpisanie aktualnego + if (VelSignalLast >= 0.0) fVelDes = Min0R(fVelDes, VelSignalLast); if (VelLimitLast >= 0.0) fVelDes = Min0R(fVelDes, VelLimitLast); @@ -1323,6 +1354,16 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN return go; }; +// modifies brake distance for low target speeds, to ease braking rate in such situations +float +TController::braking_distance_multiplier( float const Targetvelocity ) { + + if( Targetvelocity > 65.f ) { return 1.f; } + if( Targetvelocity < 5.f ) { return 1.f; } + // stretch the braking distance up to 3 times; the lower the speed, the greater the stretch + return interpolate( 3.f, 1.f, ( Targetvelocity - 5.f ) / 60.f ); +} + void TController::TablePurger() { // odtykacz: usuwa mniej istotne pozycje ze środka tabelki, aby uniknąć zatkania //(np. brak ograniczenia pomiędzy zwrotnicami, usunięte sygnały, minięte odcinki łuku) @@ -1385,7 +1426,7 @@ void TController::TablePurger() void TController::TableSort() { TSpeedPos sp_temp = TSpeedPos(); // uzywany do przenoszenia - for (std::size_t i = 0; i < (iLast - 1) && iLast > 0; i++) + for (std::size_t i = 0; i < (iLast - 1) && iLast > 0; ++i) { // pętla tylko do dwóch pozycji od końca bo ostatniej nie modyfikujemy if (sSpeedTable[i].fDist > sSpeedTable[i + 1].fDist) { // jesli pozycja wcześniejsza jest dalej to źle @@ -1394,13 +1435,13 @@ void TController::TableSort() sSpeedTable[i] = sp_temp; // jeszcze sprawdzenie czy pozycja nie była indeksowana dla eventów if (SemNextIndex == i) - SemNextIndex++; + ++SemNextIndex; else if (SemNextIndex == i + 1) - SemNextIndex--; + --SemNextIndex; if (SemNextStopIndex == i) - SemNextStopIndex++; + ++SemNextStopIndex; else if (SemNextStopIndex == i + 1) - SemNextStopIndex--; + --SemNextStopIndex; } } } @@ -1458,8 +1499,8 @@ TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, TableClear(); if( WriteLogFlag ) { - mkdir( "physicslog\\" ); - LogFile.open( std::string( "physicslog\\" + VehicleName + ".dat" ).c_str(), + _mkdir( "physicslog\\" ); + LogFile.open( std::string( "physicslog\\" + VehicleName + ".dat" ), std::ios::in | std::ios::out | std::ios::trunc ); #if LOGPRESS == 0 LogFile << std::string( " Time [s] Velocity [m/s] Acceleration [m/ss] Coupler.Dist[m] " @@ -1606,12 +1647,14 @@ void TController::Activation() ControllingSet(); // utworzenie połączenia do sterowanego pojazdu (może się zmienić) - // silnikowy dla EZT } - if (mvControlling->EngineType == - DieselEngine) // dla 2Ls150 - przed ustawieniem kierunku - można zmienić tryb pracy - if (mvControlling->ShuntModeAllow) + if( mvControlling->EngineType == DieselEngine ) { + // dla 2Ls150 - przed ustawieniem kierunku - można zmienić tryb pracy + if( mvControlling->ShuntModeAllow ) { mvControlling->CurrentSwitch( - (OrderList[OrderPos] & Shunt) || - (fMass > 224000.0)); // do tego na wzniesieniu może nie dać rady na liniowym + ( ( OrderList[ OrderPos ] & Shunt ) == Shunt ) + || ( fMass > 224000.0 ) ); // do tego na wzniesieniu może nie dać rady na liniowym + } + } // Ra: to przełączanie poniżej jest tu bez sensu mvOccupied->ActiveCab = iDirection; // aktywacja kabiny w prowadzonym pojeżdzie (silnikowy może być odwrotnie?) @@ -1706,7 +1749,80 @@ void TController::AutoRewident() } d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) } -}; + //teraz zerujemy tabelkę opóźnienia hamowania + double velstep = (mvOccupied->Vmax*0.5) / BrakeAccTableSize; + for (int i = 0; i < BrakeAccTableSize; i++) + { + fBrake_a0[i+1] = 0; + fBrake_a1[i+1] = 0; + } + d = pVehicles[0]; // pojazd na czele składu + while (d) + { // 4. Przeliczanie siły hamowania + for (int i = 0; i < BrakeAccTableSize; i++) + { + fBrake_a0[i+1] += d->MoverParameters->BrakeForceR(0.25, velstep*(1 + 2 * i)); + fBrake_a1[i+1] += d->MoverParameters->BrakeForceR(1.00, velstep*(1 + 2 * i)); + } + d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) + } + for (int i = 0; i < BrakeAccTableSize; i++) + { + fBrake_a1[i+1] -= fBrake_a0[i+1]; + fBrake_a0[i+1] /= fMass; + fBrake_a0[i + 1] += 0.001*velstep*(1 + 2 * i); + fBrake_a1[i+1] /= (12*fMass); + } + if (mvOccupied->TrainType == dt_EZT) + { + fAccThreshold = -fBrake_a0[BrakeAccTableSize] - 8 * fBrake_a1[BrakeAccTableSize]; + fBrakeReaction = 0.25; + } + else if (ustaw > 16) + { + fAccThreshold = -fBrake_a0[BrakeAccTableSize] - 4 * fBrake_a1[BrakeAccTableSize]; + fBrakeReaction = 1.00 + fLength*0.004; + } + else + { + fAccThreshold = -fBrake_a0[BrakeAccTableSize] - 1 * fBrake_a1[BrakeAccTableSize]; + fBrakeReaction = 1.00 + fLength*0.005; + } +} + +double TController::ESMVelocity(bool Main) +{ + double fCurrentCoeff = 0.9; + double fFrictionCoeff = 0.85; + double ESMVel = 9999; + int MCPN = mvControlling->MainCtrlActualPos; + int SCPN = mvControlling->ScndCtrlActualPos; + if (Main) + MCPN += 1; + else + SCPN += 1; + if ((mvControlling->RList[MCPN].ScndAct < 255)&&(mvControlling->ScndCtrlActualPos==0)) + SCPN = mvControlling->RList[MCPN].ScndAct; + double FrictionMax = mvControlling->Mass*9.81*mvControlling->Adhesive(mvControlling->RunningTrack.friction)*fFrictionCoeff; + double IF = mvControlling->Imax; + double MS = 0; + double Fmax = 0; + for (int i = 0; i < 5; i++) + { + MS = mvControlling->MomentumF(IF, IF, SCPN); + Fmax = MS * mvControlling->RList[MCPN].Bn*mvControlling->RList[MCPN].Mn * 2 / mvControlling->WheelDiameter * mvControlling->Transmision.Ratio; + IF = 0.5*IF*(1 + FrictionMax/Fmax); + } + IF = std::min(IF, mvControlling->Imax*fCurrentCoeff); + double R = mvControlling->RList[MCPN].R + mvControlling->CircuitRes + mvControlling->RList[MCPN].Mn*mvControlling->WindingRes; + double pole = mvControlling->MotorParam[SCPN].fi * + std::max(abs(IF) / (abs(IF) + mvControlling->MotorParam[SCPN].Isat) - mvControlling->MotorParam[SCPN].fi0, 0.0); + double Us = abs(mvControlling->Voltage) - IF*R; + double ns = std::max(0.0, Us / (pole*mvControlling->RList[MCPN].Mn)); + ESMVel = ns * mvControlling->WheelDiameter*M_PI*3.6/mvControlling->Transmision.Ratio; + return ESMVel; +} +; int TController::CheckDirection() { @@ -1803,8 +1919,7 @@ bool TController::CheckVehicles(TOrders user) 1 : 0); //światła manewrowe (Tb1) na pojeździe z napędem if (OrderCurrentGet() & Connect) // jeśli łączenie, skanować dalej - pVehicles[0]->fScanDist = - 5000.0; // odległość skanowania w poszukiwaniu innych pojazdów + pVehicles[0]->fScanDist = 5000.0; // odległość skanowania w poszukiwaniu innych pojazdów } else if (OrderCurrentGet() == Disconnect) if (mvOccupied->ActiveDir > 0) // jak ma kierunek do przodu @@ -1915,17 +2030,7 @@ void TController::WaitingSet(double Seconds) void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r) { // ustawienie nowej prędkości - WaitingTime = -WaitingExpireTime; // przypisujemy -WaitingExpireTime, a potem porównujemy z - // zerem - MaxVelFlag = false; // Ra: to nie jest używane - MinVelFlag = false; // Ra: to nie jest używane - /* nie używane - if ((NewVel>NewVelNext) //jeśli oczekiwana większa niż następna - || (NewVelVel)) //albo aktualna jest mniejsza niż aktualna - fProximityDist=-800.0; //droga hamowania do zmiany prędkości - else - fProximityDist=-300.0; //Ra: ujemne wartości są ignorowane - */ + WaitingTime = -WaitingExpireTime; // przypisujemy -WaitingExpireTime, a potem porównujemy z zerem if (NewVel == 0.0) // jeśli ma stanąć { if (r != stopNone) // a jest powód podany @@ -1960,23 +2065,15 @@ void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r) VelNext = NewVelNext; // prędkość przy następnym obiekcie } -/* //funkcja do niczego nie potrzebna (ew. do przesunięcia pojazdu o odległość NewDist) -bool TController::SetProximityVelocity(double NewDist,double NewVelNext) -{//informacja o prędkości w pewnej odległości -#if 0 - if (NewVelNext==0.0) - WaitingTime=0.0; //nie trzeba już czekać - //if ((NewVelNext>=0)&&((VelNext>=0)&&(NewVelNext fMinProximityDist ) + || ( mvOccupied->Vel > VelDesired + fVelPlus ) ) { + Factor += ( fBrakeReaction * ( mvOccupied->BrakeCtrlPosR < 0.5 ? 1.5 : 1 ) ) * mvOccupied->Vel / ( std::max( 0.0, ActualProximityDist ) + 1 ) * ( ( AccDesired - AbsAccS_pub ) / fAccThreshold ); + } + return Factor; } -*/ void TController::SetDriverPsyche() { @@ -2321,17 +2418,45 @@ bool TController::IncBrake() } else { if( mvOccupied->BrakeCtrlPos + 1 == mvOccupied->BrakeCtrlPosNo ) { - if( AccDesired < -1.5 ) // hamowanie nagle - OK = mvOccupied->IncBrakeLevel(); + if (AccDesired < -1.5) // hamowanie nagle + OK = mvOccupied->BrakeLevelAdd(1.0); else OK = false; } else { // dodane dla towarowego - if( mvOccupied->BrakeDelayFlag == bdelay_G ? - -AccDesired * 6.6 > std::min( 2, mvOccupied->BrakeCtrlPos ) : - true ) { - OK = mvOccupied->IncBrakeLevel(); + float pos_corr = 0; + + TDynamicObject *d; + d = pVehicles[0]; // pojazd na czele składu + while (d) + { // przeliczanie dodatkowego potrzebnego spadku ciśnienia + pos_corr+=(d->MoverParameters->Hamulec->GetCRP() - 5.0)*d->MoverParameters->TotalMass; + d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) + } + pos_corr = pos_corr / fMass * 2.5; + + if (mvOccupied->BrakeHandle == FV4a) + { + pos_corr += mvOccupied->Handle->GetCP()*0.2; + + } + double deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4.0 * (mvOccupied->BrakeCtrlPosR - 1 - pos_corr)*fBrake_a1[0]); + + if( deltaAcc > fBrake_a1[0]) + { + if( mvOccupied->BrakeCtrlPosR < 0.1 ) { + OK = mvOccupied->BrakeLevelAdd( ( + mvOccupied->BrakeDelayFlag > bdelay_G ? + 1.0 : + 1.25 ) ); + } + else + { + OK = mvOccupied->BrakeLevelAdd(0.25); + if ((deltaAcc > 5 * fBrake_a1[0]) && (mvOccupied->BrakeCtrlPosR <= 3.0)) + mvOccupied->BrakeLevelAdd(0.75); + } } else OK = false; @@ -2364,6 +2489,7 @@ bool TController::IncBrake() bool TController::DecBrake() { // zmniejszenie siły hamowania bool OK = false; + double deltaAcc = 0; switch (mvOccupied->BrakeSystem) { case Individual: @@ -2373,8 +2499,18 @@ bool TController::DecBrake() OK = mvOccupied->DecLocalBrakeLevel(1 + floor(0.5 + fabs(AccDesired))); break; case Pneumatic: - if (mvOccupied->BrakeCtrlPos > 0) - OK = mvOccupied->DecBrakeLevel(); + deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4 * (mvOccupied->BrakeCtrlPosR-1.0)*fBrake_a1[0]); + if (deltaAcc < 0) + { + if (mvOccupied->BrakeCtrlPosR > 0) + { + OK = mvOccupied->BrakeLevelAdd(-0.25); + //if ((deltaAcc < 5 * fBrake_a1[0]) && (mvOccupied->BrakeCtrlPosR >= 1.2)) + // mvOccupied->BrakeLevelAdd(-1.0); + if (mvOccupied->BrakeCtrlPosR < 0.74) + mvOccupied->BrakeLevelSet(0.0); + } + } if (!OK) OK = mvOccupied->DecLocalBrakeLevel(2); if (mvOccupied->PipePress < 3.0) @@ -2441,51 +2577,64 @@ bool TController::IncSpeed() (mvControlling->StLinFlag)) // youBy polecił dodać 2012-09-08 v367 // na pozycji 0 przejdzie, a na pozostałych będzie czekać, aż się załączą liniowe // (zgaśnie DelayCtrlFlag) - if (Ready || (iDrivigFlags & movePress)) - if (fabs(mvControlling->Im) < - (fReady < 0.4 ? mvControlling->Imin : mvControlling->IminLo)) - { // Ra: wywalał nadmiarowy, bo Im może być ujemne; jak nie odhamowany, to nie - // przesadzać z prądem - if ((mvOccupied->Vel <= 30) || - (mvControlling->Imax > mvControlling->ImaxLo) || - (fVoltage + fVoltage < - mvControlling->EnginePowerSource.CollectorParameters.MinV + - mvControlling->EnginePowerSource.CollectorParameters.MaxV)) - { // bocznik na szeregowej przy ciezkich bruttach albo przy wysokim rozruchu - // pod górę albo przy niskim napięciu - if (mvControlling->MainCtrlPos ? - mvControlling->RList[mvControlling->MainCtrlPos].R > 0.0 : - true) // oporowa - { - OK = (mvControlling->DelayCtrlFlag ? - true : - mvControlling->IncMainCtrl(1)); // kręcimy nastawnik jazdy - if ((OK) && - (mvControlling->MainCtrlPos == - 1)) // czekaj na 1 pozycji, zanim się nie włączą liniowe - iDrivigFlags |= moveIncSpeed; - else - iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi czekania - } - else // jeśli bezoporowa (z wyjątekiem 0) - OK = false; // to dać bocznik - } - else - { // przekroczone 30km/h, można wejść na jazdę równoległą - if (mvControlling->ScndCtrlPos) // jeśli ustawiony bocznik - if (mvControlling->MainCtrlPos < - mvControlling->MainCtrlPosNo - 1) // a nie jest ostatnia pozycja - mvControlling->DecScndCtrl(2); // to bocznik na zero po chamsku - // (ktoś miał to poprawić...) - OK = mvControlling->IncMainCtrl(1); - } - if ((mvControlling->MainCtrlPos > 2) && - (mvControlling->Im == 0)) // brak prądu na dalszych pozycjach - Need_TryAgain = true; // nie załączona lokomotywa albo wywalił - // nadmiarowy - else if (!OK) // nie da się wrzucić kolejnej pozycji - OK = mvControlling->IncScndCtrl(1); // to dać bocznik - } + if (Ready || (iDrivigFlags & movePress)) + { + bool scndctrl = ((mvOccupied->Vel <= 30) || + (mvControlling->Imax > mvControlling->ImaxLo) || + (fVoltage + fVoltage < + mvControlling->EnginePowerSource.CollectorParameters.MinV + + mvControlling->EnginePowerSource.CollectorParameters.MaxV) || + (mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo)); + scndctrl = ((scndctrl) && (mvControlling->MainCtrlPos > 1) && (mvControlling->RList[mvControlling->MainCtrlActualPos].R < 0.01)&& (mvControlling->ScndCtrlPos != mvControlling->ScndCtrlPosNo)); + double Vs = 99999; + if((mvControlling->MainCtrlPos != mvControlling->MainCtrlPosNo)||(mvControlling->ScndCtrlPos!=mvControlling->ScndCtrlPosNo)) + Vs = ESMVelocity(!scndctrl); + + if ((fabs(mvControlling->Im) < + (fReady < 0.4 ? mvControlling->Imin : mvControlling->IminLo))||(mvControlling->Vel>Vs)) + { // Ra: wywalał nadmiarowy, bo Im może być ujemne; jak nie odhamowany, to nie + // przesadzać z prądem + if ((mvOccupied->Vel <= 30) || + (mvControlling->Imax > mvControlling->ImaxLo) || + (fVoltage + fVoltage < + mvControlling->EnginePowerSource.CollectorParameters.MinV + + mvControlling->EnginePowerSource.CollectorParameters.MaxV)) + { // bocznik na szeregowej przy ciezkich bruttach albo przy wysokim rozruchu + // pod górę albo przy niskim napięciu + if (mvControlling->MainCtrlPos ? + mvControlling->RList[mvControlling->MainCtrlPos].R > 0.0 : + true) // oporowa + { + OK = (mvControlling->DelayCtrlFlag ? + true : + mvControlling->IncMainCtrl(1)); // kręcimy nastawnik jazdy + if ((OK) && + (mvControlling->MainCtrlPos == + 1)) // czekaj na 1 pozycji, zanim się nie włączą liniowe + iDrivigFlags |= moveIncSpeed; + else + iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi czekania + } + else // jeśli bezoporowa (z wyjątekiem 0) + OK = false; // to dać bocznik + } + else + { // przekroczone 30km/h, można wejść na jazdę równoległą + if (mvControlling->ScndCtrlPos) // jeśli ustawiony bocznik + if (mvControlling->MainCtrlPos < + mvControlling->MainCtrlPosNo - 1) // a nie jest ostatnia pozycja + mvControlling->DecScndCtrl(2); // to bocznik na zero po chamsku + // (ktoś miał to poprawić...) + OK = mvControlling->IncMainCtrl(1); + } + if ((mvControlling->MainCtrlPos > 2) && + (mvControlling->Im == 0)) // brak prądu na dalszych pozycjach + Need_TryAgain = true; // nie załączona lokomotywa albo wywalił + // nadmiarowy + else if (!OK) // nie da się wrzucić kolejnej pozycji + OK = mvControlling->IncScndCtrl(1); // to dać bocznik + } + } mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania break; case Dumb: @@ -2865,7 +3014,7 @@ bool TController::PutCommand(std::string NewCommand, double NewValue1, double Ne else TrainParams->NewName(NewCommand); // czyści tabelkę przystanków delete tsGuardSignal; - tsGuardSignal = NULL; // wywalenie kierownika + tsGuardSignal = nullptr; // wywalenie kierownika if (NewCommand != "none") { if (!TrainParams->LoadTTfile( @@ -3242,38 +3391,46 @@ void TController::PhysicsLog() } }; -bool TController::UpdateSituation(double dt) -{ // uruchamiać przynajmniej raz na sekundę - if ((iDrivigFlags & movePrimary) == 0) - return true; // pasywny nic nie robi +void +TController::UpdateSituation(double dt) { + // uruchamiać przynajmniej raz na sekundę + if( ( iDrivigFlags & movePrimary ) == 0 ) { return; } // pasywny nic nie robi - double AbsAccS; - // double VelReduced; //o ile km/h może przekroczyć dozwoloną prędkość bez hamowania - bool UpdateOK = false; - if (AIControllFlag) - { // yb: zeby EP nie musial sie bawic z ciesnieniem w PG - // if (mvOccupied->BrakeSystem==ElectroPneumatic) - // mvOccupied->PipePress=0.5; //yB: w SPKS są poprawnie zrobione pozycje - if (mvControlling->SlippingWheels) - { - mvControlling->Sandbox(true); // piasku! - // Controlling->SlippingWheels=false; //a to tu nie ma sensu, flaga używana w dalszej - // części - } - else { - // deactivate sandbox if we aren't slipping - if( mvControlling->SandDose ) { - mvControlling->Sandbox( false ); - } - } + // update timers + ElapsedTime += dt; + WaitingTime += dt; + fBrakeTime -= dt; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca + fStopTime += dt; // zliczanie czasu postoju, nie ruszy dopóki ujemne + fActionTime += dt; // czas używany przy regulacji prędkości i zamykaniu drzwi + LastReactionTime += dt; + LastUpdatedTime += dt; + + // log vehicle data + if( ( WriteLogFlag ) + && ( LastUpdatedTime > deltalog ) ) { + // zapis do pliku DAT + PhysicsLog(); + LastUpdatedTime -= deltalog; } + + if( ( fLastStopExpDist > 0.0 ) + && ( mvOccupied->DistCounter > fLastStopExpDist ) ) { + // zaktualizować wyświetlanie rozkładu + iStationStart = TrainParams->StationIndex; + fLastStopExpDist = -1.0; // usunąć licznik + } + // ABu-160305 testowanie gotowości do jazdy - // Ra: przeniesione z DynObj, skład użytkownika też jest testowany, żeby mu przekazać, że ma - // odhamować + // Ra: przeniesione z DynObj, skład użytkownika też jest testowany, żeby mu przekazać, że ma odhamować + int index = double(BrakeAccTableSize) * (mvOccupied->Vel / mvOccupied->Vmax); + index = std::min(BrakeAccTableSize, std::max(1, index)); + fBrake_a0[0] = fBrake_a0[index]; + fBrake_a1[0] = fBrake_a1[index]; Ready = true; // wstępnie gotowy fReady = 0.0; // założenie, że odhamowany fAccGravity = 0.0; // przyspieszenie wynikające z pochylenia double dy; // składowa styczna grawitacji, w przedziale <0,1> + double AbsAccS = 0; TDynamicObject *p = pVehicles[0]; // pojazd na czole składu while (p) { // sprawdzenie odhamowania wszystkich połączonych pojazdów @@ -3298,22 +3455,22 @@ bool TController::UpdateSituation(double dt) } if (fReady < p->MoverParameters->BrakePress) fReady = p->MoverParameters->BrakePress; // szukanie najbardziej zahamowanego - if ((dy = p->VectorFront().y) != 0.0) // istotne tylko dla pojazdów na pochyleniu - fAccGravity -= p->DirectionGet() * p->MoverParameters->TotalMassxg * - dy; // ciężar razy składowa styczna grawitacji + if( ( dy = p->VectorFront().y ) != 0.0 ) { + // istotne tylko dla pojazdów na pochyleniu + // ciężar razy składowa styczna grawitacji + fAccGravity -= p->DirectionGet() * p->MoverParameters->TotalMassxg * dy; + } p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła) } - if (iDirection) - fAccGravity /= - iDirection * - fMass; // siłę generują pojazdy na pochyleniu ale działa ona całość składu, więc a=F/m + if( iDirection ) { + // siłę generują pojazdy na pochyleniu ale działa ona całość składu, więc a=F/m + fAccGravity /= iDirection * fMass; + } if (!Ready) // v367: jeśli wg powyższych warunków skład nie jest odhamowany if (fAccGravity < -0.05) // jeśli ma pod górę na tyle, by się stoczyć - // if (mvOccupied->BrakePress<0.08) //to wystarczy, że zadziałają liniowe (nie ma ich - // jeszcze!!!) + // if (mvOccupied->BrakePress<0.08) //to wystarczy, że zadziałają liniowe (nie ma ich jeszcze!!!) if (fReady < 0.8) // delikatniejszy warunek, obejmuje wszystkie wagony Ready = true; //żeby uznać za odhamowany - HelpMeFlag = false; // crude way to deal with automatic door opening on W4 preventing further ride // for human-controlled vehicles with no door control and dynamic brake auto-activating with door open @@ -3324,29 +3481,52 @@ bool TController::UpdateSituation(double dt) Doors( false ); } - // Winger 020304 - if (AIControllFlag) - { - if (mvControlling->EnginePowerSource.SourceType == CurrentCollector) - { - if (mvOccupied->ScndPipePress > 4.3) // gdy główna sprężarka bezpiecznie nabije ciśnienie - mvControlling->bPantKurek3 = true; // to można przestawić kurek na zasilanie pantografów z głównej pneumatyki - fVoltage = - 0.5 * (fVoltage + - fabs(mvControlling->RunningTraction.TractionVoltage)); // uśrednione napięcie - // sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas - if (fVoltage < mvControlling->EnginePowerSource.CollectorParameters - .MinV) // gdy rozłączenie WS z powodu niskiego napięcia - if (fActionTime >= 0) // jeśli czas oczekiwania nie został ustawiony - fActionTime = - -2 - Random(10); // losowy czas oczekiwania przed ponownym załączeniem jazdy + // basic situational ai operations + // TBD, TODO: move these to main routine, if it's not neccessary for them to fire every time? + if( AIControllFlag ) { + + // wheel slip + if( mvControlling->SlippingWheels ) { + mvControlling->Sandbox( true ); // piasku! } - if (mvOccupied->Vel > 0.0) - { // jeżeli jedzie - if (iDrivigFlags & moveDoorOpened) // jeśli drzwi otwarte - if (mvOccupied->Vel > 1.0) // nie zamykać drzwi przy drganiach, bo zatrzymanie na W4 - // akceptuje niewielkie prędkości - Doors(false); + else { + // deactivate sandbox if we aren't slipping + if( mvControlling->SandDose ) { + mvControlling->Sandbox( false ); + } + } + + if (mvControlling->EnginePowerSource.SourceType == CurrentCollector) { + + if( mvOccupied->ScndPipePress > 4.3 ) { + // gdy główna sprężarka bezpiecznie nabije ciśnienie to można przestawić kurek na zasilanie pantografów z głównej pneumatyki + mvControlling->bPantKurek3 = true; + } + + // uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas + fVoltage = 0.5 * (fVoltage + std::abs(mvControlling->RunningTraction.TractionVoltage)); + if( fVoltage < mvControlling->EnginePowerSource.CollectorParameters.MinV ) { + // gdy rozłączenie WS z powodu niskiego napięcia + if( fActionTime >= 0 ) { + // jeśli czas oczekiwania nie został ustawiony, losowy czas oczekiwania przed ponownym załączeniem jazdy + fActionTime = -2.0 - Random( 10 ); + } + } + } + + if (mvOccupied->Vel > 0.0) { + // jeżeli jedzie + if( iDrivigFlags & moveDoorOpened ) { + // jeśli drzwi otwarte + if( mvOccupied->Vel > 1.0 ) { + // nie zamykać drzwi przy drganiach, bo zatrzymanie na W4 akceptuje niewielkie prędkości + Doors( false ); + } + } +/* + // NOTE: this section moved all cars to the edge of their respective roads + // it may have some use eventually for collision avoidance, + // but when enabled all the time it produces silly effect // przy prowadzeniu samochodu trzeba każdą oś odsuwać oddzielnie, inaczej kicha wychodzi if (mvOccupied->CategoryFlag & 2) // jeśli samochód // if (fabs(mvOccupied->OffsetTrackH)Dim.W) //Ra: szerokość drogi tu @@ -3355,12 +3535,12 @@ bool TController::UpdateSituation(double dt) // drogi mvOccupied->ChangeOffsetH(0.01 * mvOccupied->Vel * dt); // Ra: co to miało być, to nie wiem +*/ if (mvControlling->EnginePowerSource.SourceType == CurrentCollector) { if ((fOverhead2 >= 0.0) || iOverheadZero) { // jeśli jazda bezprądowa albo z opuszczonym pantografem - while (DecSpeed(true)) - ; // zerowanie napędu + while( DecSpeed( true ) ) { ; } // zerowanie napędu } if ((fOverhead2 > 0.0) || iOverheadDown) { // jazda z opuszczonymi pantografami @@ -3393,1361 +3573,1287 @@ bool TController::UpdateSituation(double dt) } } } - if (iDrivigFlags & moveStartHornNow) // czy ma zatrąbić przed ruszeniem? - if (Ready) // gotów do jazdy - if (iEngineActive) // jeszcze się odpalić musi - if (fStopTime >= 0) // i nie musi czekać - { // uruchomienie trąbienia - fWarningDuration = 0.3; // czas trąbienia - // if (AIControllFlag) //jak siedzi krasnoludek, to włączy trąbienie - mvOccupied->WarningSignal = - pVehicle->iHornWarning; // wysokość tonu (2=wysoki) - iDrivigFlags |= moveStartHornDone; // nie trąbić aż do ruszenia - iDrivigFlags &= ~moveStartHornNow; // trąbienie zostało zorganizowane - } - } - ElapsedTime += dt; - WaitingTime += dt; - // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca - fBrakeTime -= dt; - fStopTime += dt; // zliczanie czasu postoju, nie ruszy dopóki ujemne - fActionTime += dt; // czas używany przy regulacji prędkości i zamykaniu drzwi - if (WriteLogFlag) - { - if (LastUpdatedTime > deltalog) - { // zapis do pliku DAT - PhysicsLog(); - if (fabs(mvOccupied->V) > 0.1) // Ra: [m/s] - deltalog = 0.05; // 0.2; - else - deltalog = 0.05; // 1.0; - LastUpdatedTime = 0.0; - } - else - LastUpdatedTime = LastUpdatedTime + dt; - } - // Ra: skanowanie również dla prowadzonego ręcznie, aby podpowiedzieć prędkość - if ((LastReactionTime > std::min(ReactionTime, 2.0))) - { - // Ra: nie wiem czemu ReactionTime potrafi dostać 12 sekund, to jest przegięcie, bo przeżyna - // STÓJ - // yB: otóż jest to jedna trzecia czasu napełniania na towarowym; może się przydać przy - // wdrażaniu hamowania, żeby nie ruszało kranem jak głupie - // Ra: ale nie może się budzić co pół minuty, bo przeżyna semafory - // Ra: trzeba by tak: - // 1. Ustalić istotną odległość zainteresowania (np. 3×droga hamowania z V.max). - fBrakeDist = fDriverBraking * mvOccupied->Vel * - (40.0 + mvOccupied->Vel); // przybliżona droga hamowania - // dla hamowania -0.2 [m/ss] droga wynosi 0.389*Vel*Vel [km/h], czyli 600m dla 40km/h, 3.8km - // dla 100km/h i 9.8km dla 160km/h - // dla hamowania -0.4 [m/ss] droga wynosi 0.096*Vel*Vel [km/h], czyli 150m dla 40km/h, 1.0km - // dla 100km/h i 2.5km dla 160km/h - // ogólnie droga hamowania przy stałym opóźnieniu to Vel*Vel/(3.6*3.6*a) [m] - // fBrakeDist powinno być wyznaczane dla danego składu za pomocą sieci neuronowych, w - // zależności od prędkości i siły (ciśnienia) hamowania - // następnie w drugą stronę, z drogi hamowania i chwilowej prędkości powinno być wyznaczane - // zalecane ciśnienie - if (fMass > 1000000.0) - fBrakeDist *= 2.0; // korekta dla ciężkich, bo przeżynają - da to coś? - if (mvOccupied->BrakeDelayFlag == bdelay_G) - fBrakeDist = fBrakeDist + 2 * mvOccupied->Vel; // dla nastawienia G - // koniecznie należy wydłużyć drogę na czas reakcji - // double scanmax=(mvOccupied->Vel>0.0)?3*fDriverDist+fBrakeDist:10.0*fDriverDist; - double scanmax = (mvOccupied->Vel > 5.0) ? - 400 + fBrakeDist : - 30.0 * fDriverDist; // 1500m dla stojących pociągów; Ra 2015-01: przy - //double scanmax = Max0R(400 + fBrakeDist, 1500); - // dłuższej drodze skanowania AI jeździ spokojniej - // 2. Sprawdzić, czy tabelka pokrywa założony odcinek (nie musi, jeśli jest STOP). - // 3. Sprawdzić, czy trajektoria ruchu przechodzi przez zwrotnice - jeśli tak, to sprawdzić, - // czy stan się nie zmienił. - // 4. Ewentualnie uzupełnić tabelkę informacjami o sygnałach i ograniczeniach, jeśli się - // "zużyła". - TableCheck(scanmax); // wypełnianie tabelki i aktualizacja odległości - // 5. Sprawdzić stany sygnalizacji zapisanej w tabelce, wyznaczyć prędkości. - // 6. Z tabelki wyznaczyć krytyczną odległość i prędkość (najmniejsze przyspieszenie). - // 7. Jeśli jest inny pojazd z przodu, ewentualnie skorygować odległość i prędkość. - // 8. Ustalić częstotliwość świadomości AI (zatrzymanie precyzyjne - częściej, brak atrakcji - // - rzadziej). - if (AIControllFlag) - { // tu bedzie logika sterowania - if (mvOccupied->CommandIn.Command != "") - if( !mvOccupied->RunInternalCommand() ) { - // rozpoznaj komende bo lokomotywa jej nie rozpoznaje - RecognizeCommand(); // samo czyta komendę wstawioną do pojazdu? - } - if( mvOccupied->SecuritySystem.Status > 1 ) { - // jak zadziałało CA/SHP - if( !mvOccupied->SecuritySystemReset() ) { // to skasuj - if( ( mvOccupied->BrakeCtrlPos == 0 ) - && ( AccDesired > 0.0 ) - && ( ( TestFlag( mvOccupied->SecuritySystem.Status, s_SHPebrake ) ) - || ( TestFlag( mvOccupied->SecuritySystem.Status, s_CAebrake ) ) ) ) { - //!!! hm, może po prostu normalnie sterować hamulcem? - mvOccupied->BrakeLevelSet( 0 ); - } - } - } - // basic emergency stop handling, while at it - if( ( true == mvOccupied->EmergencyBrakeFlag ) // radio-stop - && ( mvOccupied->Vel < 0.01 ) // and actual stop - && ( true == mvOccupied->Radio ) ) { // and we didn't touch the radio yet - // turning off the radio should reset the flag, during security system check - if( m_radiocontroltime > 2.5 ) { - // arbitrary 2.5 sec delay between stop and disabling the radio - mvOccupied->Radio = false; - m_radiocontroltime = 0.0; - } - else { - m_radiocontroltime += LastReactionTime; - } - } - if( ( false == mvOccupied->Radio ) - && ( false == mvOccupied->EmergencyBrakeFlag ) ) { - // otherwise if it's safe to do so, turn the radio back on - if( m_radiocontroltime > 5.0 ) { - // arbitrary 5 sec delay before switching radio back on - mvOccupied->Radio = true; - m_radiocontroltime = 0.0; - } - else { - m_radiocontroltime += LastReactionTime; - } - } - } - switch (OrderList[OrderPos]) - { // ustalenie prędkości przy doczepianiu i odczepianiu, dystansów w pozostałych przypadkach - case Connect: // podłączanie do składu - if (iDrivigFlags & moveConnect) - { // jeśli stanął już blisko, unikając zderzenia i można próbować podłączyć - fMinProximityDist = -0.01; - fMaxProximityDist = 0.0; //[m] dojechać maksymalnie - fVelPlus = 0.5; // dopuszczalne przekroczenie prędkości na ograniczeniu bez - // hamowania - fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu - if (AIControllFlag) - { // to robi tylko AI, wersję dla człowieka trzeba dopiero zrobić - // sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec - bool ok; // true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym) - if (pVehicles[0]->DirectionGet() > 0) // jeśli sprzęg 0 - { // sprzęg 0 - próba podczepienia - if (pVehicles[0]->MoverParameters->Couplers[0].Connected) // jeśli jest coś - // wykryte (a - // chyba jest, - // nie?) - if (pVehicles[0]->MoverParameters->Attach( - 0, 2, pVehicles[0]->MoverParameters->Couplers[0].Connected, - iCoupler)) - { - // pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); - // pVehicles[0]->dsbCouplerAttach->Play(0,0,0); - } - // WriteLog("CoupleDist[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CoupleDist)+", - // Connected[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag)); - ok = (pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag == - iCoupler); // udało się? (mogło częściowo) - } - else // if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1 - { // sprzęg 1 - próba podczepienia - if (pVehicles[0]->MoverParameters->Couplers[1].Connected) // jeśli jest coś - // wykryte (a - // chyba jest, - // nie?) - if (pVehicles[0]->MoverParameters->Attach( - 1, 2, pVehicles[0]->MoverParameters->Couplers[1].Connected, - iCoupler)) - { - // pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); - // pVehicles[0]->dsbCouplerAttach->Play(0,0,0); - } - // WriteLog("CoupleDist[1]="+AnsiString(Controlling->Couplers[1].CoupleDist)+", - // Connected[0]="+AnsiString(Controlling->Couplers[1].CouplingFlag)); - ok = (pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag == - iCoupler); // udało się? (mogło częściowo) - } - if (ok) - { // jeżeli został podłączony - iCoupler = 0; // dalsza jazda manewrowa już bez łączenia - iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania - SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie - CheckVehicles(); // sprawdzić światła nowego składu - JumpToNextOrder(); // wykonanie następnej komendy - } - else - SetVelocity(2.0, 0.0); // jazda w ustawionym kierunku z prędkością 2 (18s) - } // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna - /* //Ra 2014-02: lepiej tam, bo jak tam się odźwieży skład, to tu pVehicles[0] - będzie czymś innym - else - {//jeśli człowiek ma podłączyć, to czekamy na zmianę stanu sprzęgów na końcach - dotychczasowego składu - bool ok; //true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym) - if (pVehicles[0]->DirectionGet()>0) //jeśli sprzęg 0 - ok=(pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag>0); - //==iCoupler); //udało się? (mogło częściowo) - else //if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1 - ok=(pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag>0); - //==iCoupler); //udało się? (mogło częściowo) - if (ok) - {//jeżeli został podłączony - iDrivigFlags&=~moveConnect; //zdjęcie flagi doczepiania - JumpToNextOrder(); //wykonanie następnej komendy - } - } - */ - } - else - { // jak daleko, to jazda jak dla Shunt na kolizję - fMinProximityDist = 0.0; - fMaxProximityDist = 5.0; //[m] w takim przedziale odległości powinien stanąć - fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez - // hamowania - fVelMinus = 1.0; // margines prędkości powodujący załączenie napędu - // VelReduced=5; //[km/h] - // if (mvOccupied->Vel<0.5) //jeśli już prawie stanął - if (pVehicles[0]->fTrackBlock <= - 20.0) // przy zderzeniu fTrackBlock nie jest miarodajne - iDrivigFlags |= - moveConnect; // początek podczepiania, z wyłączeniem sprawdzania fTrackBlock - } - break; - case Disconnect: // 20.07.03 - manewrowanie wagonami - fMinProximityDist = 1.0; - fMaxProximityDist = 10.0; //[m] - fVelPlus = 1.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania - fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu - if (AIControllFlag) - { - if (iVehicleCount >= 0) // jeśli była podana ilość wagonów - { - if (iDrivigFlags & movePress) // jeśli dociskanie w celu odczepienia - { // 3. faza odczepiania. - SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 - if ((mvControlling->MainCtrlPos > 0) || - (mvOccupied->BrakeSystem == ElectroPneumatic)) // jeśli jazda - { - WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); - TDynamicObject *p = - pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI - int d; // numer sprzęgu, który sprawdzamy albo odczepiamy - int n = iVehicleCount; // ile wagonów ma zostać - do - { // szukanie pojazdu do odczepienia - d = p->DirectionGet() > 0 ? - 0 : - 1; // numer sprzęgu od strony czoła składu - // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) - // //jeśli sprzęg typu wózek (za mało) - if (p->MoverParameters->Couplers[d].CouplingFlag & - ctrain_depot) // jeżeli sprzęg zablokowany - // if (p->GetTrack()->) //a nie stoi na torze warsztatowym - // (ustalić po czym poznać taki tor) - ++n; // to liczymy człony jako jeden - p->MoverParameters->BrakeReleaser( - 1); // wyluzuj pojazd, aby dało się dopychać - p->MoverParameters->BrakeLevelSet( - 0); // hamulec na zero, aby nie hamował - if (n) - { // jeśli jeszcze nie koniec - p = p->Prev(); // kolejny w stronę czoła składu (licząc od - // tyłu), bo dociskamy - if (!p) - iVehicleCount = -2, - n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone - } - } while (n--); - if (p ? p->MoverParameters->Couplers[d].CouplingFlag == 0 : true) - iVehicleCount = -2; // odczepiono, co było do odczepienia - else if (!p->Dettach(d)) // zwraca maskę bitową połączenia; usuwa - // własność pojazdów - { // tylko jeśli odepnie - WriteLog( mvOccupied->Name + " odczepiony." ); - iVehicleCount = -2; - } // a jak nie, to dociskać dalej - } - if (iVehicleCount >= 0) // zmieni się po odczepieniu - if (!mvOccupied->DecLocalBrakeLevel(1)) - { // dociśnij sklad - WriteLog( mvOccupied->Name + " dociskanie..." ); - // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę - // Ready=true; //zamiast sprawdzenia odhamowania całego składu - IncSpeed(); // dla (Ready)==false nie ruszy - } - } - if ((mvOccupied->Vel == 0.0) && !(iDrivigFlags & movePress)) - { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij - // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach - // powino zostać wyłączone) - // WriteLog("Zahamowanie składu"); - // while ((mvOccupied->BrakeCtrlPos>3)&&mvOccupied->DecBrakeLevel()); - // while ((mvOccupied->BrakeCtrlPos<3)&&mvOccupied->IncBrakeLevel()); - mvOccupied->BrakeLevelSet(mvOccupied->BrakeSystem == ElectroPneumatic ? 1 : - 3); - double p = mvOccupied->BrakePressureActual - .PipePressureVal; // tu może być 0 albo -1 nawet - if (p < 3.9) - p = 3.9; // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia - // sensu 0 oraz -1 w tym miejscu - if (mvOccupied->BrakeSystem == ElectroPneumatic ? - mvOccupied->BrakePress > 2 : - mvOccupied->PipePress < p + 0.1) - { // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na - // pozycji 3, zwyle 0.37) - if (mvOccupied->BrakeSystem == ElectroPneumatic) - mvOccupied->BrakeLevelSet(0); // wyłączenie EP, gdy wystarczy (może - // nie być potrzebne, bo na początku - // jest) - WriteLog("Luzowanie lokomotywy i zmiana kierunku"); - mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45? - mvOccupied->DecLocalBrakeLevel(10); // zwolnienie hamulca - iDrivigFlags |= movePress; // następnie będzie dociskanie - DirectionForward(mvOccupied->ActiveDir < - 0); // zmiana kierunku jazdy na przeciwny (dociskanie) - CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie - // odczepi - fStopTime = 0.0; // nie ma na co czekać z odczepianiem - } - } - } // odczepiania - else // to poniżej jeśli ilość wagonów ujemna - if (iDrivigFlags & movePress) - { // 4. faza odczepiania: zwolnij i zmień kierunek - SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie - if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da - { // ponowna zmiana kierunku - WriteLog( mvOccupied->Name + " ponowna zmiana kierunku" ); - DirectionForward(mvOccupied->ActiveDir < - 0); // zmiana kierunku jazdy na właściwy - iDrivigFlags &= ~movePress; // koniec dociskania - JumpToNextOrder(); // zmieni światła - TableClear(); // skanowanie od nowa - iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem - SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy - } - } - } - break; - case Shunt: - // na jaką odleglość i z jaką predkością ma podjechać - fMinProximityDist = 2.0; - fMaxProximityDist = 4.0; //[m] - fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania - fVelMinus = 3.0; // margines prędkości powodujący załączenie napędu - if (fVelMinus > 0.1 * fShuntVelocity) - fVelMinus = - 0.1 * - fShuntVelocity; // były problemy z jazdą np. 3km/h podczas ładowania wagonów - break; - case Obey_train: - // na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody - if (mvOccupied->CategoryFlag & 1) // jeśli pociąg - { - fMinProximityDist = 10.0; - fMaxProximityDist = - (mvOccupied->Vel > 0.0) ? - 20.0 : - 50.0; //[m] jak stanie za daleko, to niech nie dociąga paru metrów - if (iDrivigFlags & moveLate) - { - fVelMinus = 1.0; // jeśli spóźniony, to gna - fVelPlus = - 5.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania - } - else - { // gdy nie musi się sprężać - fVelMinus = - int(0.05 * VelDesired); // margines prędkości powodujący załączenie napędu - if (fVelMinus > 5.0) - fVelMinus = 5.0; - else if (fVelMinus < 1.0) - fVelMinus = 1.0; //żeby nie ruszał przy 0.1 - fVelPlus = int( - 0.5 + - 0.05 * VelDesired); // normalnie dopuszczalne przekroczenie to 5% prędkości - if (fVelPlus > 5.0) - fVelPlus = 5.0; // ale nie więcej niż 5km/h - } - } - else // samochod (sokista też) - { - fMinProximityDist = 7.0; - fMaxProximityDist = 10.0; //[m] - fVelPlus = - 10.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania - fVelMinus = 2.0; // margines prędkości powodujący załączenie napędu - } - // VelReduced=4; //[km/h] - break; - default: - fMinProximityDist = 0.01; - fMaxProximityDist = 2.0; //[m] - fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania - fVelMinus = 5.0; // margines prędkości powodujący załączenie napędu - } // switch - switch (OrderList[OrderPos]) - { // co robi maszynista - case Prepare_engine: // odpala silnik - // if (AIControllFlag) - if (PrepareEngine()) // dla użytkownika tylko sprawdza, czy uruchomił - { // gotowy do drogi? - SetDriverPsyche(); - // OrderList[OrderPos]:=Shunt; //Ra: to nie może tak być, bo scenerie robią - // Jump_to_first_order i przechodzi w manewrowy - JumpToNextOrder(); // w następnym jest Shunt albo Obey_train, moze też być - // Change_direction, Connect albo Disconnect - // if OrderList[OrderPos]<>Wait_for_Orders) - // if BrakeSystem=Pneumatic) //napelnianie uderzeniowe na wstepie - // if BrakeSubsystem=Oerlikon) - // if (BrakeCtrlPos=0)) - // DecBrakeLevel; - } - break; - case Release_engine: - if( ReleaseEngine() ) // zdana maszyna? - JumpToNextOrder(); - break; - case Jump_to_first_order: - if (OrderPos > 1) - OrderPos = 1; // w zerowym zawsze jest czekanie - else - ++OrderPos; -#if LOGORDERS - WriteLog("--> Jump_to_first_order"); - OrdersDump(); -#endif - break; - case Wait_for_orders: // jeśli czeka, też ma skanować, żeby odpalić się od semafora - /* - if ((mvOccupied->ActiveDir!=0)) - {//jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy - VelDesired=fVelMax; //wstępnie prędkość maksymalna dla pojazdu(-ów), będzie następnie - ograniczana - SetDriverPsyche(); //ustawia AccPreferred (potrzebne tu?) - //Ra: odczyt (ActualProximityDist), (VelNext) i (AccPreferred) z tabelki prędkosci - AccDesired=AccPreferred; //AccPreferred wynika z osobowości mechanika - VelNext=VelDesired; //maksymalna prędkość wynikająca z innych czynników niż - trajektoria ruchu - ActualProximityDist=scanmax; //funkcja Update() może pozostawić wartości bez zmian - //hm, kiedyś semafory wysyłały SetVelocity albo ShuntVelocity i ustawły tak VelSignal - - a teraz jak to zrobić? - TCommandType - comm=TableUpdate(mvOccupied->Vel,VelDesired,ActualProximityDist,VelNext,AccDesired); - //szukanie optymalnych wartości - } - */ - // break; - case Shunt: - case Obey_train: - case Connect: - case Disconnect: - case Change_direction: // tryby wymagające jazdy - case Change_direction | Shunt: // zmiana kierunku podczas manewrów - case Change_direction | Connect: // zmiana kierunku podczas podłączania - if (OrderList[OrderPos] != Obey_train) // spokojne manewry - { - VelSignal = Global::Min0RSpeed(VelSignal, 40); // jeśli manewry, to ograniczamy prędkość - if (AIControllFlag) - { // to poniżej tylko dla AI - if (iVehicleCount >= 0) // jeśli jest co odczepić - if (!(iDrivigFlags & movePress)) - if (mvOccupied->Vel > 0.0) - if (!iCoupler) // jeśli nie ma wcześniej potrzeby podczepienia - { - SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie - // WriteLog("Zatrzymanie w celu odczepienia"); - } - } - } - else - SetDriverPsyche(); // Ra: było w PrepareEngine(), potrzebne tu? - // no albo przypisujemy -WaitingExpireTime, albo porównujemy z WaitingExpireTime - // if - // ((VelSignal==0.0)&&(WaitingTime>WaitingExpireTime)&&(mvOccupied->RunningTrack.Velmax!=0.0)) - if (OrderList[OrderPos] & - (Shunt | Obey_train | Connect)) // odjechać sam może tylko jeśli jest w trybie jazdy - { // automatyczne ruszanie po odstaniu albo spod SBL - if ((VelSignal == 0.0) && (WaitingTime > 0.0) && - (mvOccupied->RunningTrack.Velmax != 0.0)) - { // jeśli stoi, a upłynął czas oczekiwania i tor ma niezerową prędkość - /* - if (WriteLogFlag) - { - append(AIlogFile); - writeln(AILogFile,ElapsedTime:5:2,": ",Name," V=0 waiting time expired! - (",WaitingTime:4:1,")"); - close(AILogFile); - } - */ - if ((OrderList[OrderPos] & (Obey_train | Shunt)) ? - (iDrivigFlags & moveStopHere) : - false) - WaitingTime = -WaitingExpireTime; // zakaz ruszania z miejsca bez otrzymania - // wolnej drogi - else if (mvOccupied->CategoryFlag & 1) - { // jeśli pociąg - if (AIControllFlag) - { - PrepareEngine(); // zmieni ustawiony kierunek - SetVelocity(20, 20); // jak się nastał, to niech jedzie 20km/h - WaitingTime = 0.0; - fWarningDuration = 1.5; // a zatrąbić trochę - mvOccupied->WarningSignal = 1; - } - else - SetVelocity(20, 20); // użytkownikowi zezwalamy jechać - } - else - { // samochód ma stać, aż dostanie odjazd, chyba że stoi przez kolizję - if (eStopReason == stopBlock) - if (pVehicles[0]->fTrackBlock > fDriverDist) - if (AIControllFlag) - { - PrepareEngine(); // zmieni ustawiony kierunek - SetVelocity(-1, -1); // jak się nastał, to niech jedzie - WaitingTime = 0.0; - } - else - SetVelocity(-1, - -1); // użytkownikowi pozwalamy jechać (samochodem?) - } - } - else if ((VelSignal == 0.0) && (VelNext > 0.0) && (mvOccupied->Vel < 1.0)) - if (iCoupler ? true : (iDrivigFlags & moveStopHere) == 0) // Ra: tu jest coś nie - // tak, bo bez tego - // warunku ruszało w - // manewrowym !!!! - SetVelocity(VelNext, VelNext, stopSem); // omijanie SBL - } // koniec samoistnego odjeżdżania - if (AIControllFlag) - if ((HelpMeFlag) || (mvControlling->DamageFlag > 0)) - { - HelpMeFlag = false; - /* - if (WriteLogFlag) - with Controlling do - { - append(AIlogFile); - writeln(AILogFile,ElapsedTime:5:2,": ",Name," HelpMe! - (",DamageFlag,")"); - close(AILogFile); - } - */ - } - if (AIControllFlag) - if (OrderList[OrderPos] & - Change_direction) // może być zmieszane z jeszcze jakąś komendą - { // sprobuj zmienic kierunek - SetVelocity(0, 0, stopDir); // najpierw trzeba się zatrzymać - if (mvOccupied->Vel < 0.1) - { // jeśli się zatrzymał, to zmieniamy kierunek jazdy, a nawet kabinę/człon - Activation(); // ustawienie zadanego wcześniej kierunku i ewentualne - // przemieszczenie AI - PrepareEngine(); - JumpToNextOrder(); // następnie robimy, co jest do zrobienia (Shunt albo - // Obey_train) - if (OrderList[OrderPos] & (Shunt | Connect)) // jeśli dalej mamy manewry - if ((iDrivigFlags & moveStopHere) == 0) // o ile nie stać w miejscu - { // jechać od razu w przeciwną stronę i nie trąbić z tego tytułu - iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem - SetVelocity(fShuntVelocity, fShuntVelocity); // to od razu jedziemy - } - // iDrivigFlags|=moveStartHorn; //a później już można trąbić - /* - if (WriteLogFlag) - { - append(AIlogFile); - writeln(AILogFile,ElapsedTime:5:2,": ",Name," Direction changed!"); - close(AILogFile); - } - */ - } - // else - // VelSignal:=0.0; //na wszelki wypadek niech zahamuje - } // Change_direction (tylko dla AI) - // ustalanie zadanej predkosci - if (AIControllFlag) // jeśli prowadzi AI - if (!iEngineActive) // jeśli silnik nie odpalony, to próbować naprawić - if (OrderList[OrderPos] & (Change_direction | Connect | Disconnect | Shunt | - Obey_train)) // jeśli coś ma robić - PrepareEngine(); // to niech odpala do skutku - if (iDrivigFlags & moveActive) // jeśli może skanować sygnały i reagować na komendy - { // jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy - // Ra: tu by jeszcze trzeba było wstawić uzależnienie (VelDesired) od odległości od - // przeszkody - // no chyba żeby to uwzgldnić już w (ActualProximityDist) - VelDesired = fVelMax; // wstępnie prędkość maksymalna dla pojazdu(-ów), będzie - // następnie ograniczana - if (TrainParams) // jeśli ma rozkład - if (TrainParams->TTVmax > 0.0) // i ograniczenie w rozkładzie - VelDesired = Global::Min0RSpeed(VelDesired, - TrainParams->TTVmax); // to nie przekraczać rozkladowej - SetDriverPsyche(); // ustawia AccPreferred (potrzebne tu?) - // Ra: odczyt (ActualProximityDist), (VelNext) i (AccPreferred) z tabelki prędkosci - AccDesired = AccPreferred; // AccPreferred wynika z osobowości mechanika - VelNext = VelDesired; // maksymalna prędkość wynikająca z innych czynników niż - // trajektoria ruchu - ActualProximityDist = scanmax; // funkcja Update() może pozostawić wartości bez - // zmian - // hm, kiedyś semafory wysyłały SetVelocity albo ShuntVelocity i ustawły tak - // VelSignal - a teraz jak to zrobić? - TCommandType comm = TableUpdate(VelDesired, ActualProximityDist, VelNext, - AccDesired); // szukanie optymalnych wartości - // if (VelSignal!=VelDesired) //jeżeli prędkość zalecana jest inna (ale tryb też - // może być inny) - switch (comm) - { // ustawienie VelSignal - trochę proteza = do przemyślenia - case cm_Ready: // W4 zezwolił na jazdę - TableCheck( - scanmax); // ewentualne doskanowanie trasy za W4, który zezwolił na jazdę - TableUpdate(VelDesired, ActualProximityDist, VelNext, - AccDesired); // aktualizacja po skanowaniu - // if (comm!=cm_SetVelocity) //jeśli dalej jest kolejny W4, to ma zwrócić - // cm_SetVelocity - if (VelNext == 0.0) - break; // ale jak coś z przodu zamyka, to ma stać - if (iDrivigFlags & moveStopCloser) - VelSignal = -1.0; // niech jedzie, jak W4 puściło - nie, ma czekać na - // sygnał z sygnalizatora! - case cm_SetVelocity: // od wersji 357 semafor nie budzi wyłączonej lokomotywy - if (!(OrderList[OrderPos] & - ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders - if (fabs(VelSignal) >= - 1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą - PutCommand("SetVelocity", VelSignal, VelNext, - NULL); // komenda robi dodatkowe operacje - break; - case cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy - if (!(OrderList[OrderPos] & - ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders - PutCommand("ShuntVelocity", VelSignal, VelNext, NULL); - else if (iCoupler) // jeśli jedzie w celu połączenia - SetVelocity(VelSignal, VelNext); - break; - case cm_Command: // komenda z komórki - if (!(OrderList[OrderPos] & - ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders - if (mvOccupied->Vel < 0.1) // dopiero jak stanie - // iDrivigFlags|=moveStopHere moveStopCloser) //chyba że stanął za daleko - // (SU46 w WK staje za daleko) - { - PutCommand(eSignNext->CommandGet(), eSignNext->ValueGet(1), - eSignNext->ValueGet(2), NULL); - eSignNext->StopCommandSent(); // się wykonało już - } - break; - } - if (VelNext == 0.0) - if (!(OrderList[OrderPos] & - ~(Shunt | Connect))) // jedzie w Shunt albo Connect, albo Wait_for_orders - { // jeżeli wolnej drogi nie ma, a jest w trybie manewrowym albo oczekiwania - // if - // ((OrderList[OrderPos]&Connect)?pVehicles[0]->fTrackBlock>ActualProximityDist:true) - // //pomiar odległości nie działa dobrze? - // w trybie Connect skanować do tyłu tylko jeśli przed kolejnym sygnałem nie - // ma taboru do podłączenia - // Ra 2F1H: z tym (fTrackBlock) to nie jest najlepszy pomysł, bo lepiej by - // było porównać z odległością od sygnalizatora z przodu - if ((OrderList[OrderPos] & Connect) ? (pVehicles[0]->fTrackBlock > 2000 || pVehicles[0]->fTrackBlock > FirstSemaphorDist) : - true) - if ((comm = BackwardScan()) != cm_Unknown) // jeśli w drugą można jechać - { // należy sprawdzać odległość od znalezionego sygnalizatora, - // aby w przypadku prędkości 0.1 wyciągnąć najpierw skład za - // sygnalizator - // i dopiero wtedy zmienić kierunek jazdy, oczekując podania - // prędkości >0.5 - if (comm == cm_Command) // jeśli komenda Shunt - iDrivigFlags |= - moveStopHere; // to ją odbierz bez przemieszczania się (np. - // odczep wagony po dopchnięciu do końca toru) - iDirectionOrder = -iDirection; // zmiana kierunku jazdy - OrderList[OrderPos] = TOrders(OrderList[OrderPos] | - Change_direction); // zmiana kierunku - // bez psucia - // kolejnych komend - } - } - double vel = mvOccupied->Vel; // prędkość w kierunku jazdy - if (iDirection * mvOccupied->V < 0) - vel = -vel; // ujemna, gdy jedzie w przeciwną stronę, niż powinien - if (VelDesired < 0.0) - VelDesired = fVelMax; // bo VelDesired<0 oznacza prędkość maksymalną - // Ra: jazda na widoczność - if (pVehicles[0]->fTrackBlock < 1000.0) // przy 300m stał z zapamiętaną kolizją - { // Ra 2F3F: przy jeździe pociągowej nie powinien dojeżdżać do poprzedzającego - // składu - if ((mvOccupied->CategoryFlag & 1) ? - ((OrderCurrentGet() & (Connect | Obey_train)) == Obey_train) : - false) // jeśli jesteśmy pociągiem a jazda pociągowa i nie ściąganie ze - // szlaku - { - pVehicles[0]->ABuScanObjects(pVehicles[0]->DirectionGet(), - 1000.0); // skanowanie sprawdzające - // Ra 2F3F: i jest problem, jak droga za semaforem kieruje na jakiś pojazd - // (np. w Skwarkach na ET22) - if (pVehicles[0]->fTrackBlock < 1000.0) // i jeśli nadal coś jest - if (VelNext != 0.0) // a następny sygnał zezwala na jazdę - if (pVehicles[0]->fTrackBlock < - ActualProximityDist) // i jest bliżej (tu by trzeba było wstawić - // odległość do semafora, z pominięciem SBL - VelDesired = 0.0; // to stoimy - } - else - pVehicles[0]->ABuScanObjects(pVehicles[0]->DirectionGet(), - 300.0); // skanowanie sprawdzające - } - // if (mvOccupied->Vel>=0.1) //o ile jedziemy; jak stoimy to też trzeba jakoś - // zatrzymywać - if ((iDrivigFlags & moveConnect) == 0) // przy końcówce podłączania nie hamować - { // sprawdzenie jazdy na widoczność - TCoupling *c = - pVehicles[0]->MoverParameters->Couplers + - (pVehicles[0]->DirectionGet() > 0 ? 0 : 1); // sprzęg z przodu składu - if (c->Connected) // a mamy coś z przodu - if (c->CouplingFlag == - 0) // jeśli to coś jest podłączone sprzęgiem wirtualnym - { // wyliczanie optymalnego przyspieszenia do jazdy na widoczność - double k = c->Connected->Vel; // prędkość pojazdu z przodu (zakładając, - // że jedzie w tę samą stronę!!!) - if (k < vel + 10) // porównanie modułów prędkości [km/h] - { // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej - double d = - pVehicles[0]->fTrackBlock - 0.5 * vel - - fMaxProximityDist; // odległość bezpieczna zależy od prędkości - if (d < 0) // jeśli odległość jest zbyt mała - { // AccPreferred=-0.9; //hamowanie maksymalne, bo jest za blisko - if (k < 10.0) // k - prędkość tego z przodu - { // jeśli tamten porusza się z niewielką prędkością albo stoi - if (OrderCurrentGet() & Connect) - { // jeśli spinanie, to jechać dalej - AccPreferred = 0.2; // nie hamuj - VelNext = VelDesired = 2.0; // i pakuj się na tamtego - } - else // a normalnie to hamować - { - AccPreferred = -1.0; // to hamuj maksymalnie - VelNext = VelDesired = 0.0; // i nie pakuj się na - // tamtego - } - } - else // jeśli oba jadą, to przyhamuj lekko i ogranicz prędkość - { - if (k < vel) // jak tamten jedzie wolniej - if (d < fBrakeDist) // a jest w drodze hamowania - { - if (AccPreferred > fAccThreshold) - AccPreferred = - fAccThreshold; // to przyhamuj troszkę - VelNext = VelDesired = int(k); // to chyba już sobie - // dohamuje według - // uznania - } - } - ReactionTime = 0.1; // orientuj się, bo jest goraco - } - else - { // jeśli odległość jest większa, ustalić maksymalne możliwe - // przyspieszenie (hamowanie) - k = (k * k - vel * vel) / (25.92 * d); // energia kinetyczna - // dzielona przez masę i - // drogę daje - // przyspieszenie - if (k > 0.0) - k *= 1.5; // jedź szybciej, jeśli możesz - // double ak=(c->Connected->V>0?1.0:-1.0)*c->Connected->AccS; - // //przyspieszenie tamtego - if (d < fBrakeDist) // a jest w drodze hamowania - if (k < AccPreferred) - { // jeśli nie ma innych powodów do wolniejszej jazdy - AccPreferred = k; - if (VelNext > c->Connected->Vel) - { - VelNext = - c->Connected - ->Vel; // ograniczenie do prędkości tamtego - ActualProximityDist = - d; // i odległość od tamtego jest istotniejsza - } - ReactionTime = 0.2; // zwiększ czujność - } -#if LOGVELOCITY - WriteLog("Collision: AccPreferred=" + AnsiString(k)); -#endif - } - } - } - } - // sprawdzamy możliwe ograniczenia prędkości - if (OrderCurrentGet() & (Shunt | Obey_train)) // w Connect nie, bo moveStopHere - // odnosi się do stanu po połączeniu - if (iDrivigFlags & moveStopHere) // jeśli ma czekać na wolną drogę - if (vel == 0.0) // a stoi - if (VelNext == 0.0) // a wyjazdu nie ma - VelDesired = 0.0; // to ma stać - if (fStopTime < 0) // czas postoju przed dalszą jazdą (np. na przystanku) - VelDesired = 0.0; // jak ma czekać, to nie ma jazdy - // else if (VelSignal<0) - // VelDesired=fVelMax; //ile fabryka dala (Ra: uwzględione wagony) - else if (VelSignal >= 0) // jeśli skład był zatrzymany na początku i teraz już może jechać - VelDesired = Global::Min0RSpeed(VelDesired, VelSignal); - - if (mvOccupied->RunningTrack.Velmax >= - 0) // ograniczenie prędkości z trajektorii ruchu - VelDesired = - Global::Min0RSpeed(VelDesired, - mvOccupied->RunningTrack.Velmax); // uwaga na ograniczenia szlakowej! - if (VelforDriver >= 0) // tu jest zero przy zmianie kierunku jazdy - VelDesired = Global::Min0RSpeed(VelDesired, VelforDriver); // Ra: tu może być 40, jeśli - // mechanik nie ma znajomości - // szlaaku, albo kierowca jeździ - // 70 - if (TrainParams) - if (TrainParams->CheckTrainLatency() < 5.0) - if (TrainParams->TTVmax > 0.0) - VelDesired = Global::Min0RSpeed( - VelDesired, - TrainParams - ->TTVmax); // jesli nie spozniony to nie przekraczać rozkladowej - if (VelDesired > 0.0) - if( ( ( SemNextIndex != -1 ) - && ( SemNextIndex < sSpeedTable.size() ) // BUG: index can point at non-existing slot. investigate reason(s) - && ( sSpeedTable[SemNextIndex].fVelNext != 0.0 ) ) - || ( ( iDrivigFlags & moveStopHere ) == 0 ) ) - { // jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w - // składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić - - if (iDrivigFlags & moveGuardSignal) - { // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas - // stania - iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać - - if( iDrivigFlags & moveDoorOpened ) // jeśli drzwi otwarte - if( !mvOccupied - ->DoorOpenCtrl ) // jeśli drzwi niesterowane przez maszynistę - Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika - - tsGuardSignal->Stop(); - // w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy - // bezpośrednim - // albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w - // pobliżu, a drugi radiowy, słyszalny w innych lokomotywach - // na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić - // obsługę kanałów radiowych itd. - if (!iGuardRadio) // jeśli nie przez radio - tsGuardSignal->Play( - 1.0, 0, !FreeFlyModeFlag, - pVehicle->GetPosition()); // dla true jest głośniej - else - // if (iGuardRadio==iRadioChannel) //zgodność kanału - // if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu - // (albo może mieć radio przenośne) - kierownik mógłby powtarzać - // przy braku reakcji - if (SquareMagnitude(pVehicle->GetPosition() - - Global::pCameraPosition) < - 2000 * 2000) // w odległości mniejszej niż 2km - tsGuardSignal->Play( - 1.0, 0, true, - pVehicle->GetPosition()); // dźwięk niby przez radio - } - } - if (mvOccupied->V == 0.0) - AbsAccS = fAccGravity; // Ra 2014-03: jesli skład stoi, to działa na niego - // składowa styczna grawitacji - else - AbsAccS = iDirection * mvOccupied->AccS; // przyspieszenie chwilowe, liczone -// jako różnica skierowanej prędkości w -// czasie -// if (mvOccupied->V<0.0) AbsAccS=-AbsAccS; //Ra 2014-03: to trzeba przemyśleć -// if (vel<0) //jeżeli się stacza w tył; 2014-03: to jest bez sensu, bo vel>=0 -// AbsAccS=-AbsAccS; //to przyspieszenie też działa wtedy w nieodpowiednią stronę -// AbsAccS+=fAccGravity; //wypadkowe przyspieszenie (czy to ma sens?) -#if LOGVELOCITY - // WriteLog("VelDesired="+AnsiString(VelDesired)+", - // VelSignal="+AnsiString(VelSignal)); - WriteLog("Vel=" + AnsiString(vel) + ", AbsAccS=" + AnsiString(AbsAccS) + - ", AccGrav=" + AnsiString(fAccGravity)); -#endif - // ustalanie zadanego przyspieszenia - //(ActualProximityDist) - odległość do miejsca zmniejszenia prędkości - //(AccPreferred) - wynika z psychyki oraz uwzglęnia już ewentualne zderzenie z - // pojazdem z przodu, ujemne gdy należy hamować - //(AccDesired) - uwzględnia sygnały na drodze ruchu, ujemne gdy należy hamować - //(fAccGravity) - chwilowe przspieszenie grawitacyjne, ujemne działa przeciwnie do - // zadanego kierunku jazdy - //(AbsAccS) - chwilowe przyspieszenie pojazu (uwzględnia grawitację), ujemne działa - // przeciwnie do zadanego kierunku jazdy - //(AccDesired) porównujemy z (fAccGravity) albo (AbsAccS) - // if ((VelNext>=0.0)&&(ActualProximityDist>=0)&&(mvOccupied->Vel>=VelNext)) //gdy - // zbliza sie i jest za szybko do NOWEGO - if ((VelNext >= 0.0) && (ActualProximityDist <= scanmax) && (vel >= VelNext)) - { // gdy zbliża się i jest za szybki do nowej prędkości, albo stoi na zatrzymaniu - if (vel > 0.0) - { // jeśli jedzie - if ((vel < VelNext) ? - (ActualProximityDist > fMaxProximityDist * (1 + 0.1 * vel)) : - false) // dojedz do semafora/przeszkody - { // jeśli jedzie wolniej niż można i jest wystarczająco daleko, to można - // przyspieszyć - if (AccPreferred > 0.0) // jeśli nie ma zawalidrogi - AccDesired = AccPreferred; - // VelDesired:=Min0R(VelDesired,VelReduced+VelNext); - } - else if (ActualProximityDist > fMinProximityDist) - { // jedzie szybciej, niż trzeba na końcu ActualProximityDist, ale jeszcze - // jest daleko - if (vel < - VelNext + 40.0) // dwustopniowe hamowanie - niski przy małej różnicy - { // jeśli jedzie wolniej niż VelNext+35km/h //Ra: 40, żeby nie - // kombinował na zwrotnicach - if (VelNext == 0.0) - { // jeśli ma się zatrzymać, musi być to robione precyzyjnie i - // skutecznie - if (ActualProximityDist < - fMaxProximityDist) // jak minął już maksymalny dystans - { // po prostu hamuj (niski stopień) //ma stanąć, a jest w - // drodze hamowania albo ma jechać - AccDesired = fAccThreshold; // hamowanie tak, aby stanąć - VelDesired = 0.0; // Min0R(VelDesired,VelNext); - } - else if (ActualProximityDist > fBrakeDist) - { // jeśli ma stanąć, a mieści się w drodze hamowania - if (vel < 10.0) // jeśli prędkość jest łatwa do zatrzymania - { // tu jest trochę problem, bo do punktu zatrzymania dobija - // na raty - // AccDesired=AccDesired<0.0?0.0:0.1*AccPreferred; - AccDesired = AccPreferred; // proteza trochę; jak tu - // wychodzi 0.05, to loki - // mają problem utrzymać - // takie przyspieszenie - } - else if (vel <= 30.0) // trzymaj 30 km/h - AccDesired = Min0R(0.5 * AccDesired, - AccPreferred); // jak jest tu 0.5, to - // samochody się - // dobijają do siebie - else - AccDesired = 0.0; - } - else // 25.92 (=3.6*3.6*2) - przelicznik z km/h na m/s - if (vel < - VelNext + fVelPlus) // jeśli niewielkie przekroczenie - // AccDesired=0.0; - AccDesired = Min0R(0.0, AccPreferred); // proteza trochę: to - // niech nie hamuje, - // chyba że coś z - // przodu - else - AccDesired = -(vel * vel) / - (25.92 * (ActualProximityDist + - 0.1)); //-fMinProximityDist));//-0.1; - ////mniejsze opóźnienie przy - // małej różnicy - ReactionTime = 0.1; // i orientuj się szybciej, jak masz stanąć - } - else if (vel < VelNext + fVelPlus) // jeśli niewielkie - // przekroczenie, ale ma jechać - AccDesired = - Min0R(0.0, AccPreferred); // to olej (zacznij luzować) - else - { // jeśli większe przekroczenie niż fVelPlus [km/h], ale ma jechać - // Ra 2F1I: jak było (VelNext+fVelPlus) tu, to hamował zbyt - // późno przed 40, a potem zbyt mocno i zwalniał do 30 - AccDesired = (VelNext * VelNext - vel * vel) / - (25.92 * ActualProximityDist + - 0.1); // mniejsze opóźnienie przy małej różnicy - if (ActualProximityDist < fMaxProximityDist) - ReactionTime = 0.1; // i orientuj się szybciej, jeśli w - // krytycznym przedziale - } - } - else // przy dużej różnicy wysoki stopień (1,25 potrzebnego opoznienia) - AccDesired = (VelNext * VelNext - vel * vel) / - (20.73 * ActualProximityDist + - 0.1); // najpierw hamuje mocniej, potem zluzuje - if (AccPreferred < AccDesired) - AccDesired = AccPreferred; //(1+abs(AccDesired)) - // ReactionTime=0.5*mvOccupied->BrakeDelay[2+2*mvOccupied->BrakeDelayFlag]; - // //aby szybkosc hamowania zalezala od przyspieszenia i opoznienia - // hamulcow - // fBrakeTime=0.5*mvOccupied->BrakeDelay[2+2*mvOccupied->BrakeDelayFlag]; - // //aby szybkosc hamowania zalezala od przyspieszenia i opoznienia - // hamulcow - } - else - { // jest bliżej niż fMinProximityDist - VelDesired = - Min0R(VelDesired, VelNext); // utrzymuj predkosc bo juz blisko - if (vel < - VelNext + fVelPlus) // jeśli niewielkie przekroczenie, ale ma jechać - AccDesired = Min0R(0.0, AccPreferred); // to olej (zacznij luzować) - ReactionTime = 0.1; // i orientuj się szybciej - } - } - else // zatrzymany (vel==0.0) - // if (iDrivigFlags&moveStopHere) //to nie dotyczy podczepiania - // if ((VelNext>0.0)||(ActualProximityDist>fMaxProximityDist*1.2)) - if (VelNext > 0.0) - AccDesired = AccPreferred; // można jechać - else // jeśli daleko jechać nie można - if (ActualProximityDist > - fMaxProximityDist) // ale ma kawałek do sygnalizatora - { // if ((iDrivigFlags&moveStopHere)?false:AccPreferred>0) - if (AccPreferred > 0) - AccDesired = AccPreferred; // dociagnij do semafora; - else - VelDesired = 0.0; //,AccDesired=-fabs(fAccGravity); //stoj (hamuj z siłą - // równą składowej stycznej grawitacji) - } - else - VelDesired = 0.0; // VelNext=0 i stoi bliżej niż fMaxProximityDist - } - else // gdy jedzie wolniej niż potrzeba, albo nie ma przeszkód na drodze - AccDesired = (VelDesired != 0.0 ? AccPreferred : -0.01); // normalna jazda - // koniec predkosci nastepnej - if ((VelDesired >= 0.0) && - (vel > VelDesired)) // jesli jedzie za szybko do AKTUALNEGO - if (VelDesired == 0.0) // jesli stoj, to hamuj, ale i tak juz za pozno :) - AccDesired = -0.9; // hamuj solidnie - else if ((vel < VelDesired + fVelPlus)) // o 5 km/h to olej - { - if ((AccDesired > 0.0)) - AccDesired = 0.0; - } - else - AccDesired = fAccThreshold; // hamuj tak średnio - // koniec predkosci aktualnej - if (fAccThreshold > -0.3) // bez sensu, ale dla towarowych korzystnie - { // Ra 2014-03: to nie uwzględnia odległości i zaczyna hamować, jak tylko zobaczy - // W4 - if ((AccDesired > 0.0) && - (VelNext >= 0.0)) // wybieg bądź lekkie hamowanie, warunki byly zamienione - if (vel > VelNext + 100.0) // lepiej zaczac hamowac - AccDesired = fAccThreshold; - else if (vel > VelNext + 70.0) - AccDesired = 0.0; // nie spiesz się, bo będzie hamowanie - // koniec wybiegu i hamowania - } - if (AIControllFlag) - { // część wykonawcza tylko dla AI, dla człowieka jedynie napisy - if (mvControlling->ConvOvldFlag || - !mvControlling->Mains) // WS może wywalić z powodu błędu w drutach - { // wywalił bezpiecznik nadmiarowy przetwornicy - // while (DecSpeed()); //zerowanie napędu - // Controlling->ConvOvldFlag=false; //reset nadmiarowego - PrepareEngine(); // próba ponownego załączenia - } - // włączanie bezpiecznika - if ((mvControlling->EngineType == ElectricSeriesMotor) || - (mvControlling->TrainType & dt_EZT) || - (mvControlling->EngineType == DieselElectric)) - if (mvControlling->FuseFlag || Need_TryAgain) - { - Need_TryAgain = - false; // true, jeśli druga pozycja w elektryku nie załapała - // if (!Controlling->DecScndCtrl(1)) //kręcenie po mału - // if (!Controlling->DecMainCtrl(1)) //nastawnik jazdy na 0 - mvControlling->DecScndCtrl(2); // nastawnik bocznikowania na 0 - mvControlling->DecMainCtrl(2); // nastawnik jazdy na 0 - mvControlling->MainSwitch( - true); // Ra: dodałem, bo EN57 stawały po wywaleniu - if (!mvControlling->FuseOn()) - HelpMeFlag = true; - else - { - ++iDriverFailCount; - if (iDriverFailCount > maxdriverfails) - Psyche = Easyman; - if (iDriverFailCount > maxdriverfails * 2) - SetDriverPsyche(); - } - } - if (mvOccupied->BrakeSystem == Pneumatic) // napełnianie uderzeniowe - if (mvOccupied->BrakeHandle == FV4a) - { - if (mvOccupied->BrakeCtrlPos == -2) - mvOccupied->BrakeLevelSet(0); - // if - // ((mvOccupied->BrakeCtrlPos<0)&&(mvOccupied->PipeBrakePress<0.01))//{(CntrlPipePress-(Volume/BrakeVVolume/10)<0.01)}) - // mvOccupied->IncBrakeLevel(); - if ((mvOccupied->PipePress < 3.0) && (AccDesired > -0.03)) - mvOccupied->BrakeReleaser(1); - if ((mvOccupied->BrakeCtrlPos == 0) && (AbsAccS < 0.0) && - (AccDesired > -0.03)) - // if FuzzyLogicAI(CntrlPipePress-PipePress,0.01,1)) - // if - // ((mvOccupied->BrakePress>0.5)&&(mvOccupied->LocalBrakePos<0.5))//{((Volume/BrakeVVolume/10)<0.485)}) - if ((mvOccupied->EqvtPipePress < 4.95) && - (fReady > 0.35)) //{((Volume/BrakeVVolume/10)<0.485)}) - { - if (iDrivigFlags & - moveOerlikons) // a reszta składu jest na to gotowa - mvOccupied->BrakeLevelSet(-1); // napełnianie w Oerlikonie - } - else if (Need_BrakeRelease) - { - Need_BrakeRelease = false; - mvOccupied->BrakeReleaser(1); - // DecBrakeLevel(); //z tym by jeszcze miało jakiś sens - } - // if - // ((mvOccupied->BrakeCtrlPos<0)&&(mvOccupied->BrakePress<0.3))//{(CntrlPipePress-(Volume/BrakeVVolume/10)<0.01)}) - if ((mvOccupied->BrakeCtrlPos < 0) && - (mvOccupied->EqvtPipePress > - (fReady < 0.25 ? - 5.1 : - 5.2))) //{(CntrlPipePress-(Volume/BrakeVVolume/10)<0.01)}) - mvOccupied->IncBrakeLevel(); - } -#if LOGVELOCITY - WriteLog("Dist=" + FloatToStrF(ActualProximityDist, ffFixed, 7, 1) + - ", VelDesired=" + FloatToStrF(VelDesired, ffFixed, 7, 1) + - ", AccDesired=" + FloatToStrF(AccDesired, ffFixed, 7, 3) + - ", VelSignal=" + AnsiString(VelSignal) + ", VelNext=" + - AnsiString(VelNext)); -#endif - if (AccDesired > 0.1) - if (vel < 10.0) // Ra 2F1H: jeśli prędkość jest mała, a można przyspieszać, - // to nie ograniczać przyspieszenia do 0.5m/ss - AccDesired = 0.9; // przy małych prędkościach może być trudno utrzymać - // małe przyspieszenie - // Ra 2F1I: wyłączyć kiedyś to uśrednianie i przeanalizować skanowanie, czemu - // migocze - if (AccDesired > -0.15) // hamowania lepeiej nie uśredniać - AccDesired = fAccDesiredAv = - 0.2 * AccDesired + - 0.8 * fAccDesiredAv; // uśrednione, żeby ograniczyć migotanie - if (VelDesired == 0.0) - if (AccDesired >= -0.01) - AccDesired = -0.01; // Ra 2F1J: jeszcze jedna prowizoryczna łatka - if (AccDesired >= 0.0) - if (iDrivigFlags & movePress) - mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę - może być więcej! - else if (OrderList[OrderPos] != - Disconnect) // przy odłączaniu nie zwalniamy tu hamulca - if ((AccDesired > 0.0) || - (fAccGravity * fAccGravity < - 0.001)) // luzuj tylko na plaskim lub przy ruszaniu - { - while (DecBrake()) - ; // jeśli przyspieszamy, to nie hamujemy - if (mvOccupied->BrakePress > 0.4) - mvOccupied->BrakeReleaser( - 1); // wyluzuj lokomotywę, to szybciej ruszymy - } - // margines dla prędkości jest doliczany tylko jeśli oczekiwana prędkość jest - // większa od 5km/h - if (!(iDrivigFlags & movePress)) - { // jeśli nie dociskanie - if (AccDesired < -0.1) - while (DecSpeed()) - ; // jeśli hamujemy, to nie przyspieszamy - else if (((fAccGravity < -0.01) ? AccDesired < 0.0 : - AbsAccS > AccDesired) || - (vel > VelDesired)) // jak za bardzo przyspiesza albo prędkość - // przekroczona - DecSpeed(); // pojedyncze cofnięcie pozycji, bo na zero to przesada - } - // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej - // zwiekszanie predkosci - // Ra 2F1H: jest konflikt histerezy pomiędzy nastawioną pozycją a uzyskiwanym - // przyspieszeniem - utrzymanie pozycji powoduje przekroczenie przyspieszenia - if (AbsAccS < - AccDesired) // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz - if (vel < VelDesired - fVelMinus) // jeśli prędkość w kierunku czoła jest - // mniejsza od dozwolonej o margines - if ((ActualProximityDist > fMaxProximityDist) ? true : (vel < VelNext)) - IncSpeed(); // to można przyspieszyć - // if ((AbsAccS0) and - // (EngineType=ElectricSeriesMotor) - // and (RList[MainCtrlPos].R>0.0) and (not DelayCtrlFlag)) - // if (ImTrainType & - dt_EZT) // właściwie, to warunek powinien być na działający EP - { // Ra: to dobrze hamuje EP w EZT - if ((AccDesired <= fAccThreshold) ? // jeśli hamować - u góry ustawia się - // hamowanie na fAccThreshold - ((AbsAccS > AccDesired) || (mvOccupied->BrakeCtrlPos < 0)) : - false) // hamować bardziej, gdy aktualne opóźnienie hamowania - // mniejsze niż (AccDesired) - IncBrake(); - else if (OrderList[OrderPos] != - Disconnect) // przy odłączaniu nie zwalniamy tu hamulca - if (AbsAccS < - AccDesired - - 0.05) // jeśli opóźnienie większe od wymaganego (z histerezą) - { // luzowanie, gdy za dużo - if (mvOccupied->BrakeCtrlPos >= 0) - DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu - } - else if (mvOccupied->Handle->TimeEP) - { - if (mvOccupied->Handle->GetPos(bh_EPR) - - mvOccupied->Handle->GetPos(bh_EPN) < - 0.1) - mvOccupied->SwitchEPBrake(0); - else - mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_EPN)); - } - // else if (mvOccupied->BrakeCtrlPos<0) IncBrake(); //ustawienie - // jazdy (pozycja 0) - // else if (mvOccupied->BrakeCtrlPos>0) DecBrake(); - } - else - { // a stara wersja w miarę dobrze działa na składy wagonowe - // if (mvOccupied->Handle->Time) - // mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_MB)); - // //najwyzej sobie przestawi - if (((fAccGravity < -0.05) && (vel < 0)) || - ((AccDesired < fAccGravity - 0.1) && - (AbsAccS > - AccDesired + 0.05))) // u góry ustawia się hamowanie na fAccThreshold - // if not MinVelFlag) - if (fBrakeTime < 0 ? true : (AccDesired < fAccGravity - 0.3) || - (mvOccupied->BrakeCtrlPos <= 0)) - if (!IncBrake()) // jeśli upłynął czas reakcji hamulca, chyba że - // nagłe albo luzował - MinVelFlag = true; - else - { - MinVelFlag = false; - fBrakeTime = - 3 + - 0.5 * - (mvOccupied - ->BrakeDelay[2 + 2 * mvOccupied->BrakeDelayFlag] - - 3); - // Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do - // zatrzymania jest mniejszy - fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1 - } - if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - 0.2)) - // if ((AccDesired<0.0)&&(AbsAccSBrakeDelay[1 + 2 * mvOccupied->BrakeDelayFlag]) / 3.0; - fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1 - } - } - // Mietek-end1 - SpeedSet(); // ciągla regulacja prędkości -#if LOGVELOCITY - WriteLog("BrakePos=" + AnsiString(mvOccupied->BrakeCtrlPos) + ", MainCtrl=" + - AnsiString(mvControlling->MainCtrlPos)); -#endif - - /* //Ra: mamy teraz wskażnik na człon silnikowy, gorzej jak są dwa w - ukrotnieniu... - //zapobieganie poslizgowi w czlonie silnikowym; Ra: Couplers[1] powinno - być - if (Controlling->Couplers[0].Connected!=NULL) - if (TestFlag(Controlling->Couplers[0].CouplingFlag,ctrain_controll)) - if (Controlling->Couplers[0].Connected->SlippingWheels) - if (Controlling->ScndCtrlPos>0?!Controlling->DecScndCtrl(1):true) - { - if (!Controlling->DecMainCtrl(1)) - if (mvOccupied->BrakeCtrlPos==mvOccupied->BrakeCtrlPosNo) - mvOccupied->DecBrakeLevel(); - ++iDriverFailCount; - } - */ - // zapobieganie poslizgowi u nas - if (mvControlling->SlippingWheels) - { - if (!mvControlling->DecScndCtrl(2)) // bocznik na zero - mvControlling->DecMainCtrl(1); - if (mvOccupied->BrakeCtrlPos == - mvOccupied->BrakeCtrlPosNo) // jeśli ostatnia pozycja hamowania - mvOccupied->DecBrakeLevel(); // to cofnij hamulec - else - mvControlling->AntiSlippingButton(); - ++iDriverFailCount; - mvControlling->SlippingWheels = false; // flaga już wykorzystana - } - if (iDriverFailCount > maxdriverfails) - { - Psyche = Easyman; - if (iDriverFailCount > maxdriverfails * 2) - SetDriverPsyche(); - } - } // if (AIControllFlag) - else - { // tu mozna dać komunikaty tekstowe albo słowne: przyspiesz, hamuj (lekko, - // średnio, mocno) - } - } // kierunek różny od zera - else - { // tutaj, gdy pojazd jest wyłączony - if (!AIControllFlag) // jeśli sterowanie jest w gestii użytkownika - if (mvOccupied->Battery) // czy użytkownik załączył baterię? - if (mvOccupied->ActiveDir) // czy ustawił kierunek - { // jeśli tak, to uruchomienie skanowania - CheckVehicles(); // sprawdzić skład - TableClear(); // resetowanie tabelki skanowania - PrepareEngine(); // uruchomienie - } - } - if (AIControllFlag) - { // odhamowywanie składu po zatrzymaniu i zabezpieczanie lokomotywy - if ((OrderList[OrderPos] & (Disconnect | Connect)) == - 0) // przy (p)odłączaniu nie zwalniamy tu hamulca - if ((mvOccupied->V == 0.0) && ((VelDesired == 0.0) || (AccDesired == 0.0))) - if ((mvOccupied->BrakeCtrlPos < 1) || !mvOccupied->DecBrakeLevel()) - mvOccupied->IncLocalBrakeLevel(1); // dodatkowy na pozycję 1 - } - break; // rzeczy robione przy jezdzie - } // switch (OrderList[OrderPos]) - // kasowanie licznika czasu - LastReactionTime = 0.0; - UpdateOK = true; - } // if ((LastReactionTime>Min0R(ReactionTime,2.0))) - else - LastReactionTime += dt; - - if ((fLastStopExpDist > 0.0) && (mvOccupied->DistCounter > fLastStopExpDist)) - { - iStationStart = TrainParams->StationIndex; // zaktualizować wyświetlanie rozkładu - fLastStopExpDist = -1.0f; // usunąć licznik - } - - if (AIControllFlag) - { - if (fWarningDuration > 0.0) // jeśli pozostało coś do wytrąbienia - { // trąbienie trwa nadal - fWarningDuration = fWarningDuration - dt; - if (fWarningDuration < 0.05) + // horn control + if( fWarningDuration > 0.0 ) { + // jeśli pozostało coś do wytrąbienia trąbienie trwa nadal + fWarningDuration -= dt; + if( fWarningDuration < 0.05 ) mvOccupied->WarningSignal = 0; // a tu się kończy - if (ReactionTime > fWarningDuration) - ReactionTime = - fWarningDuration; // wcześniejszy przebłysk świadomości, by zakończyć trąbienie } - if (mvOccupied->Vel >= - 3.0) // jesli jedzie, można odblokować trąbienie, bo się wtedy nie włączy - { + if( mvOccupied->Vel >= 3.0 ) { + // jesli jedzie, można odblokować trąbienie, bo się wtedy nie włączy iDrivigFlags &= ~moveStartHornDone; // zatrąbi dopiero jak następnym razem stanie iDrivigFlags |= moveStartHorn; // i trąbić przed następnym ruszeniem } - return UpdateOK; + + if( ( true == TestFlag( iDrivigFlags, moveStartHornNow ) ) + && ( true == Ready ) + && ( iEngineActive != 0 ) + && ( fStopTime >= 0 ) ) { + // uruchomienie trąbienia przed ruszeniem + fWarningDuration = 0.3; // czas trąbienia + mvOccupied->WarningSignal = pVehicle->iHornWarning; // wysokość tonu (2=wysoki) + iDrivigFlags |= moveStartHornDone; // nie trąbić aż do ruszenia + iDrivigFlags &= ~moveStartHornNow; // trąbienie zostało zorganizowane + } } - else // if (AIControllFlag) - return false; // AI nie obsługuje + + // main ai update routine + auto const reactiontime = std::min( ReactionTime, 2.0 ); + if( LastReactionTime < reactiontime ) { return; } + + LastReactionTime -= reactiontime; + // Ra: nie wiem czemu ReactionTime potrafi dostać 12 sekund, to jest przegięcie, bo przeżyna STÓJ + // yB: otóż jest to jedna trzecia czasu napełniania na towarowym; może się przydać przy + // wdrażaniu hamowania, żeby nie ruszało kranem jak głupie + // Ra: ale nie może się budzić co pół minuty, bo przeżyna semafory + // Ra: trzeba by tak: + // 1. Ustalić istotną odległość zainteresowania (np. 3×droga hamowania z V.max). + // dla hamowania -0.2 [m/ss] droga wynosi 0.389*Vel*Vel [km/h], czyli 600m dla 40km/h, 3.8km + // dla 100km/h i 9.8km dla 160km/h + // dla hamowania -0.4 [m/ss] droga wynosi 0.096*Vel*Vel [km/h], czyli 150m dla 40km/h, 1.0km + // dla 100km/h i 2.5km dla 160km/h + // ogólnie droga hamowania przy stałym opóźnieniu to Vel*Vel/(3.6*3.6*a) [m] + // fBrakeDist powinno być wyznaczane dla danego składu za pomocą sieci neuronowych, w + // zależności od prędkości i siły (ciśnienia) hamowania + // następnie w drugą stronę, z drogi hamowania i chwilowej prędkości powinno być wyznaczane zalecane ciśnienie + + // przybliżona droga hamowania + fBrakeDist = fDriverBraking * mvOccupied->Vel * ( 40.0 + mvOccupied->Vel ); + if( fMass > 1000000.0 ) { + // korekta dla ciężkich, bo przeżynają - da to coś? + fBrakeDist *= 2.0; + } + if( ( -fAccThreshold > 0.05 ) + && ( mvOccupied->CategoryFlag == 1 ) ) { + fBrakeDist = mvOccupied->Vel * mvOccupied->Vel / 25.92 / -fAccThreshold; + } + if( mvOccupied->BrakeDelayFlag == bdelay_G ) { + // dla nastawienia G koniecznie należy wydłużyć drogę na czas reakcji + fBrakeDist += 2 * mvOccupied->Vel; + } + // route scan + double routescanrange = ( + mvOccupied->Vel > 5.0 ? + 400 + fBrakeDist : + 30.0 * fDriverDist ); // 1500m dla stojących pociągów; + // Ra 2015-01: przy dłuższej drodze skanowania AI jeździ spokojniej + // 2. Sprawdzić, czy tabelka pokrywa założony odcinek (nie musi, jeśli jest STOP). + // 3. Sprawdzić, czy trajektoria ruchu przechodzi przez zwrotnice - jeśli tak, to sprawdzić, + // czy stan się nie zmienił. + // 4. Ewentualnie uzupełnić tabelkę informacjami o sygnałach i ograniczeniach, jeśli się + // "zużyła". + TableCheck( routescanrange ); // wypełnianie tabelki i aktualizacja odległości + // 5. Sprawdzić stany sygnalizacji zapisanej w tabelce, wyznaczyć prędkości. + // 6. Z tabelki wyznaczyć krytyczną odległość i prędkość (najmniejsze przyspieszenie). + // 7. Jeśli jest inny pojazd z przodu, ewentualnie skorygować odległość i prędkość. + // 8. Ustalić częstotliwość świadomości AI (zatrzymanie precyzyjne - częściej, brak atrakcji + // - rzadziej). + + // check for potential colliders + { + auto const collisionscanrange = 300.0 + fBrakeDist; + auto rearvehicle = ( + pVehicles[ 0 ] == pVehicles[ 1 ] ? + pVehicles[ 0 ] : + pVehicles[ 1 ] ); + // for moving vehicle determine heading from velocity; for standing fall back on the set direction + if( ( mvOccupied->V != 0.0 ? + mvOccupied->V > 0.0 : + iDirection > 0 ) ) { + // towards coupler 0 + if( ( mvOccupied->V * iDirection < 0.0 ) + || ( ( rearvehicle->NextConnected != nullptr ) + && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 1 : 0 ) ].CouplingFlag == ctrain_virtual ) ) ) { + // scan behind if we're moving backward, or if we had something connected there and are moving away + rearvehicle->ABuScanObjects( ( + pVehicle->DirectionGet() == rearvehicle->DirectionGet() ? + -1 : + 1 ), + fMaxProximityDist ); + } + pVehicles[ 0 ]->ABuScanObjects( ( + pVehicle->DirectionGet() == pVehicles[ 0 ]->DirectionGet() ? + 1 : + -1 ), + collisionscanrange ); + } + else { + // towards coupler 1 + if( ( mvOccupied->V * iDirection < 0.0 ) + || ( ( rearvehicle->PrevConnected != nullptr ) + && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 0 : 1 ) ].CouplingFlag == ctrain_virtual ) ) ) { + // scan behind if we're moving backward, or if we had something connected there and are moving away + rearvehicle->ABuScanObjects( ( + pVehicle->DirectionGet() == rearvehicle->DirectionGet() ? + 1 : + -1 ), + fMaxProximityDist ); + } + pVehicles[ 0 ]->ABuScanObjects( ( + pVehicle->DirectionGet() == pVehicles[ 0 ]->DirectionGet() ? + -1 : + 1 ), + collisionscanrange ); + } + } + + // tu bedzie logika sterowania + if (AIControllFlag) { + + if (mvOccupied->CommandIn.Command != "") + if( !mvOccupied->RunInternalCommand() ) { + // rozpoznaj komende bo lokomotywa jej nie rozpoznaje + RecognizeCommand(); // samo czyta komendę wstawioną do pojazdu? + } + if( mvOccupied->SecuritySystem.Status > 1 ) { + // jak zadziałało CA/SHP + if( !mvOccupied->SecuritySystemReset() ) { // to skasuj + if( ( mvOccupied->BrakeCtrlPos == 0 ) + && ( AccDesired > 0.0 ) + && ( ( TestFlag( mvOccupied->SecuritySystem.Status, s_SHPebrake ) ) + || ( TestFlag( mvOccupied->SecuritySystem.Status, s_CAebrake ) ) ) ) { + //!!! hm, może po prostu normalnie sterować hamulcem? + mvOccupied->BrakeLevelSet( 0 ); + } + } + } + // basic emergency stop handling, while at it + if( ( true == mvOccupied->RadioStopFlag ) // radio-stop + && ( mvOccupied->Vel == 0.0 ) // and actual stop + && ( true == mvOccupied->Radio ) ) { // and we didn't touch the radio yet + // turning off the radio should reset the flag, during security system check + if( m_radiocontroltime > 5.0 ) { + // arbitrary delay between stop and disabling the radio + mvOccupied->Radio = false; + m_radiocontroltime = 0.0; + } + else { + m_radiocontroltime += reactiontime; + } + } + if( ( false == mvOccupied->Radio ) + && ( false == mvOccupied->RadioStopFlag ) ) { + // otherwise if it's safe to do so, turn the radio back on + if( m_radiocontroltime > 10.0 ) { + // arbitrary 5 sec delay before switching radio back on + mvOccupied->Radio = true; + m_radiocontroltime = 0.0; + } + else { + m_radiocontroltime += reactiontime; + } + } + } + + switch (OrderList[OrderPos]) + { // ustalenie prędkości przy doczepianiu i odczepianiu, dystansów w pozostałych przypadkach + case Connect: { + // podłączanie do składu + if (iDrivigFlags & moveConnect) { + // jeśli stanął już blisko, unikając zderzenia i można próbować podłączyć + fMinProximityDist = -0.5; + fMaxProximityDist = 0.0; //[m] dojechać maksymalnie + fVelPlus = 0.5; // dopuszczalne przekroczenie prędkości na ograniczeniu bez + // hamowania + fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu + if (AIControllFlag) + { // to robi tylko AI, wersję dla człowieka trzeba dopiero zrobić + // sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec + bool ok; // true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym) + if (pVehicles[0]->DirectionGet() > 0) // jeśli sprzęg 0 + { // sprzęg 0 - próba podczepienia + if (pVehicles[0]->MoverParameters->Couplers[0].Connected) // jeśli jest coś + // wykryte (a + // chyba jest, + // nie?) + if (pVehicles[0]->MoverParameters->Attach( + 0, 2, pVehicles[0]->MoverParameters->Couplers[0].Connected, + iCoupler)) + { + // pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); + // pVehicles[0]->dsbCouplerAttach->Play(0,0,0); + } + // WriteLog("CoupleDist[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CoupleDist)+", + // Connected[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag)); + ok = (pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag == + iCoupler); // udało się? (mogło częściowo) + } + else // if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1 + { // sprzęg 1 - próba podczepienia + if( pVehicles[ 0 ]->MoverParameters->Couplers[ 1 ].Connected ) { + // jeśli jest coś wykryte (a chyba jest, nie?) + if( pVehicles[ 0 ]->MoverParameters->Attach( + 1, 2, pVehicles[ 0 ]->MoverParameters->Couplers[ 1 ].Connected, + iCoupler ) ) { + // pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); + // pVehicles[0]->dsbCouplerAttach->Play(0,0,0); + } + } + // WriteLog("CoupleDist[1]="+AnsiString(Controlling->Couplers[1].CoupleDist)+", + // Connected[0]="+AnsiString(Controlling->Couplers[1].CouplingFlag)); + ok = (pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag == + iCoupler); // udało się? (mogło częściowo) + } + if (ok) + { // jeżeli został podłączony + iCoupler = 0; // dalsza jazda manewrowa już bez łączenia + iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania + SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie + CheckVehicles(); // sprawdzić światła nowego składu + JumpToNextOrder(); // wykonanie następnej komendy + } + else + SetVelocity(2.0, 0.0); // jazda w ustawionym kierunku z prędkością 2 (18s) + } // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna + } + else { + // jak daleko, to jazda jak dla Shunt na kolizję + fMinProximityDist = 2.0; + fMaxProximityDist = 5.0; //[m] w takim przedziale odległości powinien stanąć + fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + fVelMinus = 1.0; // margines prędkości powodujący załączenie napędu + if( pVehicles[ 0 ]->fTrackBlock <= 20.0 ) { + // przy zderzeniu fTrackBlock nie jest miarodajne + // początek podczepiania, z wyłączeniem sprawdzania fTrackBlock + iDrivigFlags |= moveConnect; + } + } + break; + } + case Disconnect: { + // 20.07.03 - manewrowanie wagonami + fMinProximityDist = 1.0; + fMaxProximityDist = 10.0; //[m] + fVelPlus = 1.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu + if (AIControllFlag) { + if (iVehicleCount >= 0) { + // jeśli była podana ilość wagonów + if (iDrivigFlags & movePress) { + // jeśli dociskanie w celu odczepienia + // 3. faza odczepiania. + SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 + if ((mvControlling->MainCtrlPos > 0) || + (mvOccupied->BrakeSystem == ElectroPneumatic)) // jeśli jazda + { + WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); + TDynamicObject *p = + pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI + int d; // numer sprzęgu, który sprawdzamy albo odczepiamy + int n = iVehicleCount; // ile wagonów ma zostać + do + { // szukanie pojazdu do odczepienia + d = p->DirectionGet() > 0 ? + 0 : + 1; // numer sprzęgu od strony czoła składu + // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) + // //jeśli sprzęg typu wózek (za mało) + if (p->MoverParameters->Couplers[d].CouplingFlag & + ctrain_depot) // jeżeli sprzęg zablokowany + // if (p->GetTrack()->) //a nie stoi na torze warsztatowym + // (ustalić po czym poznać taki tor) + ++n; // to liczymy człony jako jeden + p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać + p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował + if (n) + { // jeśli jeszcze nie koniec + p = p->Prev(); // kolejny w stronę czoła składu (licząc od + // tyłu), bo dociskamy + if (!p) + iVehicleCount = -2, + n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone + } + } while (n--); + if( p ? p->MoverParameters->Couplers[ d ].CouplingFlag == coupling::faux : true ) { + // no target, or already just virtual coupling + WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); + iVehicleCount = -2; // odczepiono, co było do odczepienia + } else if ( p->Dettach(d) == coupling::faux ) { + // tylko jeśli odepnie + WriteLog( mvOccupied->Name + " odczepiony." ); + iVehicleCount = -2; + } // a jak nie, to dociskać dalej + } + if (iVehicleCount >= 0) // zmieni się po odczepieniu + if (!mvOccupied->DecLocalBrakeLevel(1)) + { // dociśnij sklad + WriteLog( mvOccupied->Name + " dociskanie..." ); + // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę + // Ready=true; //zamiast sprawdzenia odhamowania całego składu + IncSpeed(); // dla (Ready)==false nie ruszy + } + } + if ((mvOccupied->Vel == 0.0) && !(iDrivigFlags & movePress)) + { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij + // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach + // powino zostać wyłączone) + // WriteLog("Zahamowanie składu"); + mvOccupied->BrakeLevelSet( + mvOccupied->BrakeSystem == ElectroPneumatic ? + 1 : + 3 ); + double p = mvOccupied->BrakePressureActual.PipePressureVal; + if( p < 3.9 ) { + // tu może być 0 albo -1 nawet + // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia sensu 0 oraz -1 w tym miejscu + p = 3.9; + } + if (mvOccupied->BrakeSystem == ElectroPneumatic ? + mvOccupied->BrakePress > 2 : + mvOccupied->PipePress < p + 0.1) + { // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na + // pozycji 3, zwyle 0.37) + if (mvOccupied->BrakeSystem == ElectroPneumatic) + mvOccupied->BrakeLevelSet(0); // wyłączenie EP, gdy wystarczy (może + // nie być potrzebne, bo na początku + // jest) + WriteLog("Luzowanie lokomotywy i zmiana kierunku"); + mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45? + mvOccupied->DecLocalBrakeLevel(10); // zwolnienie hamulca + iDrivigFlags |= movePress; // następnie będzie dociskanie + DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na przeciwny (dociskanie) + CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie odczepi + fStopTime = 0.0; // nie ma na co czekać z odczepianiem + } + } + } // odczepiania + else // to poniżej jeśli ilość wagonów ujemna + if (iDrivigFlags & movePress) + { // 4. faza odczepiania: zwolnij i zmień kierunek + SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie + if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da + { // ponowna zmiana kierunku + WriteLog( mvOccupied->Name + " ponowna zmiana kierunku" ); + DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na właściwy + iDrivigFlags &= ~movePress; // koniec dociskania + JumpToNextOrder(); // zmieni światła + TableClear(); // skanowanie od nowa + iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem + SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy + } + } + } + break; + } + case Shunt: { + // na jaką odleglość i z jaką predkością ma podjechać + fMinProximityDist = 5.0; + fMaxProximityDist = 10.0; //[m] + if( pVehicles[ 0 ] != pVehicles[ 1 ] ) { + // for larger consists increase margins to account for slower braking etc + // NOTE: this will affect also multi-unit vehicles TBD: is this what we want? + fMinProximityDist *= 2.0; + fMaxProximityDist *= 2.0; + } + fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + // margines prędkości powodujący załączenie napędu + // były problemy z jazdą np. 3km/h podczas ładowania wagonów + fVelMinus = std::min( 0.1 * fShuntVelocity, 3.0 ); + break; + } + case Obey_train: { + // na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody + if( mvOccupied->CategoryFlag & 1 ) { + // jeśli pociąg + fMinProximityDist = 15.0; + fMaxProximityDist = + ( mvOccupied->Vel > 0.0 ) ? + 25.0 : + 50.0; //[m] jak stanie za daleko, to niech nie dociąga paru metrów + if( iDrivigFlags & moveLate ) { + // jeśli spóźniony, to gna + fVelMinus = 1.0; + // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + fVelPlus = 5.0; + } + else { + // gdy nie musi się sprężać + // margines prędkości powodujący załączenie napędu; min 1.0 żeby nie ruszał przy 0.1 + fVelMinus = clamp( std::round( 0.05 * VelDesired ), 1.0, 5.0 ); + // normalnie dopuszczalne przekroczenie to 5% prędkości ale nie więcej niż 5km/h + // bottom margin raised to 2 km/h to give the AI more leeway at low speed limits + fVelPlus = clamp( std::ceil( 0.05 * VelDesired ), 2.0, 5.0 ); + } + } + else { + // samochod (sokista też) + fMinProximityDist = std::max( 4.0, mvOccupied->Vel * 0.2 ); + fMaxProximityDist = std::max( 8.0, mvOccupied->Vel * 0.375 ); //[m] + // margines prędkości powodujący załączenie napędu + fVelMinus = 2.0; + // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + fVelPlus = std::min( 10.0, mvOccupied->Vel * 0.1 ); + } + break; + } + default: { + fMinProximityDist = 0.01; + fMaxProximityDist = 2.0; //[m] + fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + fVelMinus = 5.0; // margines prędkości powodujący załączenie napędu + } + } // switch OrderList[OrderPos] + + switch (OrderList[OrderPos]) + { // co robi maszynista + case Prepare_engine: // odpala silnik + // if (AIControllFlag) + if (PrepareEngine()) // dla użytkownika tylko sprawdza, czy uruchomił + { // gotowy do drogi? + SetDriverPsyche(); + // OrderList[OrderPos]:=Shunt; //Ra: to nie może tak być, bo scenerie robią + // Jump_to_first_order i przechodzi w manewrowy + JumpToNextOrder(); // w następnym jest Shunt albo Obey_train, moze też być + // Change_direction, Connect albo Disconnect + // if OrderList[OrderPos]<>Wait_for_Orders) + // if BrakeSystem=Pneumatic) //napelnianie uderzeniowe na wstepie + // if BrakeSubsystem=Oerlikon) + // if (BrakeCtrlPos=0)) + // DecBrakeLevel; + } + break; + case Release_engine: + if( ReleaseEngine() ) // zdana maszyna? + JumpToNextOrder(); + break; + case Jump_to_first_order: + if (OrderPos > 1) + OrderPos = 1; // w zerowym zawsze jest czekanie + else + ++OrderPos; +#if LOGORDERS + WriteLog("--> Jump_to_first_order"); + OrdersDump(); +#endif + break; + case Wait_for_orders: // jeśli czeka, też ma skanować, żeby odpalić się od semafora + case Shunt: + case Obey_train: + case Connect: + case Disconnect: + case Change_direction: // tryby wymagające jazdy + case Change_direction | Shunt: // zmiana kierunku podczas manewrów + case Change_direction | Connect: // zmiana kierunku podczas podłączania + if (OrderList[OrderPos] != Obey_train) // spokojne manewry + { + VelSignal = Global::Min0RSpeed(VelSignal, 40); // jeśli manewry, to ograniczamy prędkość + + // NOTE: this section should be moved to disconnect or plain removed, but it seems to be (mis)used by some scenarios + // to keep vehicle idling without moving :| + if( ( true == AIControllFlag ) + && ( iVehicleCount >= 0 ) + && ( false == TestFlag( iDrivigFlags, movePress ) ) + && ( iCoupler == 0 ) +// && ( mvOccupied->Vel > 0.0 ) + && ( pVehicle->PrevConnected == nullptr ) + && ( pVehicle->NextConnected == nullptr ) ) { + SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie + // WriteLog("Zatrzymanie w celu odczepienia"); + AccPreferred = std::min( 0.0, AccPreferred ); + } + + } + else + SetDriverPsyche(); // Ra: było w PrepareEngine(), potrzebne tu? + // no albo przypisujemy -WaitingExpireTime, albo porównujemy z WaitingExpireTime + // if + // ((VelSignal==0.0)&&(WaitingTime>WaitingExpireTime)&&(mvOccupied->RunningTrack.Velmax!=0.0)) + if (OrderList[OrderPos] & + (Shunt | Obey_train | Connect)) // odjechać sam może tylko jeśli jest w trybie jazdy + { // automatyczne ruszanie po odstaniu albo spod SBL + if ((VelSignal == 0.0) && (WaitingTime > 0.0) && + (mvOccupied->RunningTrack.Velmax != 0.0)) + { // jeśli stoi, a upłynął czas oczekiwania i tor ma niezerową prędkość + /* + if (WriteLogFlag) + { + append(AIlogFile); + writeln(AILogFile,ElapsedTime:5:2,": ",Name," V=0 waiting time expired! + (",WaitingTime:4:1,")"); + close(AILogFile); + } + */ + if ((OrderList[OrderPos] & (Obey_train | Shunt)) ? + (iDrivigFlags & moveStopHere) : + false) + WaitingTime = -WaitingExpireTime; // zakaz ruszania z miejsca bez otrzymania + // wolnej drogi + else if (mvOccupied->CategoryFlag & 1) + { // jeśli pociąg + if (AIControllFlag) + { + PrepareEngine(); // zmieni ustawiony kierunek + SetVelocity(20, 20); // jak się nastał, to niech jedzie 20km/h + WaitingTime = 0.0; + fWarningDuration = 1.5; // a zatrąbić trochę + mvOccupied->WarningSignal = 1; + } + else + SetVelocity(20, 20); // użytkownikowi zezwalamy jechać + } + else + { // samochód ma stać, aż dostanie odjazd, chyba że stoi przez kolizję + if (eStopReason == stopBlock) + if (pVehicles[0]->fTrackBlock > fDriverDist) + if (AIControllFlag) + { + PrepareEngine(); // zmieni ustawiony kierunek + SetVelocity(-1, -1); // jak się nastał, to niech jedzie + WaitingTime = 0.0; + } + else { + // użytkownikowi pozwalamy jechać (samochodem?) + SetVelocity( -1, -1 ); + } + } + } + else if( ( VelSignal == 0.0 ) && ( VelNext > 0.0 ) && ( mvOccupied->Vel < 1.0 ) ) { + if( iCoupler ? true : ( iDrivigFlags & moveStopHere ) == 0 ) { + // Ra: tu jest coś nie tak, bo bez tego warunku ruszało w manewrowym !!!! + SetVelocity( VelNext, VelNext, stopSem ); // omijanie SBL + } + } + } // koniec samoistnego odjeżdżania + + if( ( true == AIControllFlag) + && ( true == TestFlag( OrderList[ OrderPos ], Change_direction ) ) ) { + // sprobuj zmienic kierunek (może być zmieszane z jeszcze jakąś komendą) + SetVelocity( 0, 0, stopDir ); // najpierw trzeba się zatrzymać + if( mvOccupied->Vel < 0.1 ) { + // jeśli się zatrzymał, to zmieniamy kierunek jazdy, a nawet kabinę/człon + Activation(); // ustawienie zadanego wcześniej kierunku i ewentualne przemieszczenie AI + PrepareEngine(); + JumpToNextOrder(); // następnie robimy, co jest do zrobienia (Shunt albo Obey_train) + if( OrderList[ OrderPos ] & ( Shunt | Connect ) ) { + // jeśli dalej mamy manewry + if( false == TestFlag( iDrivigFlags, moveStopHere ) ) { + // o ile nie ma stać w miejscu, + // jechać od razu w przeciwną stronę i nie trąbić z tego tytułu: + iDrivigFlags &= ~moveStartHorn; + SetVelocity( fShuntVelocity, fShuntVelocity ); + } + } + } + } // Change_direction (tylko dla AI) + + if( ( true == AIControllFlag ) + && ( iEngineActive == 0 ) + && ( OrderList[ OrderPos ] & ( Change_direction | Connect | Disconnect | Shunt | Obey_train ) ) ) { + // jeśli coś ma robić to niech odpala do skutku + PrepareEngine(); + } + + // ustalanie zadanej predkosci + if (iDrivigFlags & moveActive) // jeśli może skanować sygnały i reagować na komendy + { // jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy + // Ra: tu by jeszcze trzeba było wstawić uzależnienie (VelDesired) od odległości od + // przeszkody + // no chyba żeby to uwzgldnić już w (ActualProximityDist) + VelDesired = fVelMax; // wstępnie prędkość maksymalna dla pojazdu(-ów), będzie + // następnie ograniczana + if( ( TrainParams ) + && ( TrainParams->TTVmax > 0.0 ) ) { + // jeśli ma rozkład i ograniczenie w rozkładzie to nie przekraczać rozkladowej + VelDesired = Global::Min0RSpeed( VelDesired, TrainParams->TTVmax ); + } + + SetDriverPsyche(); // ustawia AccPreferred (potrzebne tu?) + + // szukanie optymalnych wartości + AccDesired = AccPreferred; // AccPreferred wynika z osobowości mechanika + VelNext = VelDesired; // maksymalna prędkość wynikająca z innych czynników niż trajektoria ruchu + ActualProximityDist = routescanrange; // funkcja Update() może pozostawić wartości bez zmian + // Ra: odczyt (ActualProximityDist), (VelNext) i (AccPreferred) z tabelki prędkosci + TCommandType comm = TableUpdate(VelDesired, ActualProximityDist, VelNext, AccDesired); + + switch (comm) + { // ustawienie VelSignal - trochę proteza = do przemyślenia + case cm_Ready: // W4 zezwolił na jazdę + // ewentualne doskanowanie trasy za W4, który zezwolił na jazdę + TableCheck( routescanrange); + TableUpdate(VelDesired, ActualProximityDist, VelNext, AccDesired); // aktualizacja po skanowaniu + if (VelNext == 0.0) + break; // ale jak coś z przodu zamyka, to ma stać + if (iDrivigFlags & moveStopCloser) + VelSignal = -1.0; // ma czekać na sygnał z sygnalizatora! + case cm_SetVelocity: // od wersji 357 semafor nie budzi wyłączonej lokomotywy + if (!(OrderList[OrderPos] & + ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders + if (fabs(VelSignal) >= + 1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą + PutCommand("SetVelocity", VelSignal, VelNext, + NULL); // komenda robi dodatkowe operacje + break; + case cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy + if (!(OrderList[OrderPos] & + ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders + PutCommand("ShuntVelocity", VelSignal, VelNext, NULL); + else if (iCoupler) // jeśli jedzie w celu połączenia + SetVelocity(VelSignal, VelNext); + break; + case cm_Command: // komenda z komórki + if( !( OrderList[ OrderPos ] & ~( Obey_train | Shunt ) ) ) { + // jedzie w dowolnym trybie albo Wait_for_orders + if( mvOccupied->Vel < 0.1 ) { + // dopiero jak stanie + PutCommand( eSignNext->CommandGet(), eSignNext->ValueGet( 1 ), eSignNext->ValueGet( 2 ), nullptr ); + eSignNext->StopCommandSent(); // się wykonało już + } + } + break; + } + + if( VelNext == 0.0 ) { + if( !( OrderList[ OrderPos ] & ~( Shunt | Connect ) ) ) { + // jedzie w Shunt albo Connect, albo Wait_for_orders + // w trybie Connect skanować do tyłu tylko jeśli przed kolejnym sygnałem nie ma taboru do podłączenia + // Ra 2F1H: z tym (fTrackBlock) to nie jest najlepszy pomysł, bo lepiej by + // było porównać z odległością od sygnalizatora z przodu + if( ( OrderList[ OrderPos ] & Connect ) ? + ( pVehicles[ 0 ]->fTrackBlock > 2000 || pVehicles[ 0 ]->fTrackBlock > FirstSemaphorDist ) : + true ) { + if( ( comm = BackwardScan() ) != cm_Unknown ) { + // jeśli w drugą można jechać + // należy sprawdzać odległość od znalezionego sygnalizatora, + // aby w przypadku prędkości 0.1 wyciągnąć najpierw skład za sygnalizator + // i dopiero wtedy zmienić kierunek jazdy, oczekując podania prędkości >0.5 + if( comm == cm_Command ) { + // jeśli komenda Shunt to ją odbierz bez przemieszczania się (np. odczep wagony po dopchnięciu do końca toru) + iDrivigFlags |= moveStopHere; + } + iDirectionOrder = -iDirection; // zmiana kierunku jazdy + // zmiana kierunku bez psucia kolejnych komend + OrderList[ OrderPos ] = TOrders( OrderList[ OrderPos ] | Change_direction ); + } + } + } + } + + double vel = mvOccupied->Vel; // prędkość w kierunku jazdy + if (iDirection * mvOccupied->V < 0) + vel = -vel; // ujemna, gdy jedzie w przeciwną stronę, niż powinien + if (VelDesired < 0.0) + VelDesired = fVelMax; // bo VelDesired<0 oznacza prędkość maksymalną + + // Ra: jazda na widoczność +/* + // condition disabled, it'd prevent setting reduced acceleration in the last connect stage + if ((iDrivigFlags & moveConnect) == 0) // przy końcówce podłączania nie hamować +*/ + { // sprawdzenie jazdy na widoczność + auto const vehicle = pVehicles[ 0 ]; // base calculactions off relevant end of the consist + auto const coupler = + vehicle->MoverParameters->Couplers + ( + vehicle->DirectionGet() > 0 ? + 0 : + 1 ); // sprzęg z przodu składu + if( ( coupler->Connected ) + && ( coupler->CouplingFlag == 0 ) ) { + // mamy coś z przodu podłączone sprzęgiem wirtualnym + // wyliczanie optymalnego przyspieszenia do jazdy na widoczność + ActualProximityDist = std::min( + ActualProximityDist, + vehicle->fTrackBlock - ( + mvOccupied->CategoryFlag & 2 ? + fMinProximityDist : // cars can bunch up tighter + fMaxProximityDist ) ); // other vehicle types less so + double k = coupler->Connected->Vel; // prędkość pojazdu z przodu (zakładając, + // że jedzie w tę samą stronę!!!) + if( k < vel + 10 ) { + // porównanie modułów prędkości [km/h] + // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej + double const distance = vehicle->fTrackBlock - fMaxProximityDist - ( fBrakeDist * 1.15 ); // odległość bezpieczna zależy od prędkości + if( distance < 0 ) { + // jeśli odległość jest zbyt mała + if( k < 10.0 ) // k - prędkość tego z przodu + { // jeśli tamten porusza się z niewielką prędkością albo stoi + if( OrderCurrentGet() & Connect ) { + // jeśli spinanie, to jechać dalej + AccPreferred = std::min( 0.25, AccPreferred ); // nie hamuj + VelDesired = Global::Min0RSpeed( 20.0, VelDesired ); + VelNext = 2.0; // i pakuj się na tamtego + } + else { + // a normalnie to hamować + VelNext = 0.0; + if( vehicle->fTrackBlock <= fMinProximityDist ) { + VelDesired = 0.0; + } + } + } + else { + // jeśli oba jadą, to przyhamuj lekko i ogranicz prędkość + if( vehicle->fTrackBlock < ( + mvOccupied->CategoryFlag & 2 ? + fMaxProximityDist + 0.5 * vel : // cars + 2.0 * fMaxProximityDist + 2.0 * vel ) ) { //others + // jak tamten jedzie wolniej a jest w drodze hamowania + AccPreferred = std::min( -0.9, AccPreferred ); + VelNext = Global::Min0RSpeed( std::round( k ) - 5.0, VelDesired ); + if( vehicle->fTrackBlock <= ( + mvOccupied->CategoryFlag & 2 ? + fMaxProximityDist : // cars + 2.0 * fMaxProximityDist ) ) { //others + // try to force speed change if obstacle is really close + VelDesired = VelNext; + } + } + } + ReactionTime = ( + vel != 0.0 ? + 0.1 : // orientuj się, bo jest goraco + 2.0 ); // we're already standing still, so take it easy + } + else { + if( OrderCurrentGet() & Connect ) { + // if there's something nearby in the connect mode don't speed up too much + VelDesired = Global::Min0RSpeed( 20.0, VelDesired ); + } + } + } + } + } + + // sprawdzamy możliwe ograniczenia prędkości + if (OrderCurrentGet() & (Shunt | Obey_train)) // w Connect nie, bo moveStopHere + // odnosi się do stanu po połączeniu + if (iDrivigFlags & moveStopHere) // jeśli ma czekać na wolną drogę + if (vel == 0.0) // a stoi + if (VelNext == 0.0) // a wyjazdu nie ma + VelDesired = 0.0; // to ma stać + if (fStopTime < 0) // czas postoju przed dalszą jazdą (np. na przystanku) + VelDesired = 0.0; // jak ma czekać, to nie ma jazdy + // else if (VelSignal<0) + // VelDesired=fVelMax; //ile fabryka dala (Ra: uwzględione wagony) + else if (VelSignal >= 0) // jeśli skład był zatrzymany na początku i teraz już może jechać + VelDesired = Global::Min0RSpeed(VelDesired, VelSignal); + + if (mvOccupied->RunningTrack.Velmax >= + 0) // ograniczenie prędkości z trajektorii ruchu + VelDesired = + Global::Min0RSpeed(VelDesired, + mvOccupied->RunningTrack.Velmax); // uwaga na ograniczenia szlakowej! + if (VelforDriver >= 0) // tu jest zero przy zmianie kierunku jazdy + VelDesired = Global::Min0RSpeed(VelDesired, VelforDriver); // Ra: tu może być 40, jeśli + // mechanik nie ma znajomości + // szlaaku, albo kierowca jeździ + // 70 + if (TrainParams) + if (TrainParams->CheckTrainLatency() < 5.0) + if (TrainParams->TTVmax > 0.0) + VelDesired = Global::Min0RSpeed( + VelDesired, + TrainParams + ->TTVmax); // jesli nie spozniony to nie przekraczać rozkladowej + if (VelDesired > 0.0) + if( ( ( iDrivigFlags & moveStopHere ) == 0 ) + || ( ( SemNextIndex != -1 ) + && ( SemNextIndex < sSpeedTable.size() ) // BUG: index can point at non-existing slot. investigate reason(s) + && ( sSpeedTable[SemNextIndex].fVelNext != 0.0 ) ) ) { + // jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w + // składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić + if (iDrivigFlags & moveGuardSignal) + { // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas + // stania + iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać + + if( iDrivigFlags & moveDoorOpened ) // jeśli drzwi otwarte + if( !mvOccupied + ->DoorOpenCtrl ) // jeśli drzwi niesterowane przez maszynistę + Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika + + tsGuardSignal->Stop(); + // w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy + // bezpośrednim + // albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w + // pobliżu, a drugi radiowy, słyszalny w innych lokomotywach + // na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić + // obsługę kanałów radiowych itd. + if( !iGuardRadio ) { + // jeśli nie przez radio + tsGuardSignal->Play( 1.0, 0, !FreeFlyModeFlag, pVehicle->GetPosition() ); // dla true jest głośniej + } + else { + // if (iGuardRadio==iRadioChannel) //zgodność kanału + // if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu + // (albo może mieć radio przenośne) - kierownik mógłby powtarzać + // przy braku reakcji + if( SquareMagnitude( pVehicle->GetPosition() - Global::pCameraPosition ) < 2000 * 2000 ) { + // w odległości mniejszej niż 2km + tsGuardSignal->Play( 1.0, 0, true, pVehicle->GetPosition() ); // dźwięk niby przez radio + } + } + } + } + if( mvOccupied->V == 0.0 ) { + // Ra 2014-03: jesli skład stoi, to działa na niego składowa styczna grawitacji + AbsAccS = fAccGravity; + } + else { + AbsAccS = 0; + TDynamicObject *d = pVehicles[0]; // pojazd na czele składu + while (d) + { + AbsAccS += d->MoverParameters->TotalMass * d->MoverParameters->AccS * iDirection; + d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) + } + AbsAccS /= fMass; + } + AbsAccS_pub = AbsAccS; + +#if LOGVELOCITY + // WriteLog("VelDesired="+AnsiString(VelDesired)+", + // VelSignal="+AnsiString(VelSignal)); + WriteLog("Vel=" + AnsiString(vel) + ", AbsAccS=" + AnsiString(AbsAccS) + + ", AccGrav=" + AnsiString(fAccGravity)); +#endif + // ustalanie zadanego przyspieszenia + //(ActualProximityDist) - odległość do miejsca zmniejszenia prędkości + //(AccPreferred) - wynika z psychyki oraz uwzglęnia już ewentualne zderzenie z + // pojazdem z przodu, ujemne gdy należy hamować + //(AccDesired) - uwzględnia sygnały na drodze ruchu, ujemne gdy należy hamować + //(fAccGravity) - chwilowe przspieszenie grawitacyjne, ujemne działa przeciwnie do + // zadanego kierunku jazdy + //(AbsAccS) - chwilowe przyspieszenie pojazu (uwzględnia grawitację), ujemne działa + // przeciwnie do zadanego kierunku jazdy + //(AccDesired) porównujemy z (fAccGravity) albo (AbsAccS) + if( ( VelNext >= 0.0 ) + && ( ActualProximityDist <= routescanrange ) + && ( vel >= VelNext ) ) { + // gdy zbliża się i jest za szybki do nowej prędkości, albo stoi na zatrzymaniu + if (vel > 0.0) { + // jeśli jedzie + if( ( vel < VelNext ) + && ( ActualProximityDist > fMaxProximityDist * ( 1.0 + 0.1 * vel ) ) ) { + // jeśli jedzie wolniej niż można i jest wystarczająco daleko, to można przyspieszyć + if( AccPreferred > 0.0 ) { + // jeśli nie ma zawalidrogi dojedz do semafora/przeszkody + AccDesired = AccPreferred; + } + } + else if (ActualProximityDist > fMinProximityDist) { + // jedzie szybciej, niż trzeba na końcu ActualProximityDist, ale jeszcze jest daleko + if (ActualProximityDist < fMaxProximityDist) { + // jak minął już maksymalny dystans po prostu hamuj (niski stopień) + // ma stanąć, a jest w drodze hamowania albo ma jechać +/* + VelDesired = Global::Min0RSpeed( VelDesired, VelNext ); +*/ + if( VelNext == 0.0 ) { + // hamowanie tak, aby stanąć + VelDesired = VelNext; + AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) ); + AccDesired = std::min( AccDesired, fAccThreshold ); + } + } + else { + // przy dużej różnicy wysoki stopień (1,00 potrzebnego opoznienia) + // ensure some minimal coasting speed, otherwise a vehicle entering this zone at very low speed will be crawling forever + auto const brakingpointoffset = VelNext * braking_distance_multiplier( VelNext ); + AccDesired = std::min( + AccDesired, + ( VelNext * VelNext - vel * vel ) + / ( 25.92 + * std::max( + ActualProximityDist - brakingpointoffset, + std::min( + ActualProximityDist, + brakingpointoffset ) ) + + 0.1 ) ); // najpierw hamuje mocniej, potem zluzuje + } + AccDesired = std::min( AccDesired, AccPreferred ); + } + else { + // jest bliżej niż fMinProximityDist + // utrzymuj predkosc bo juz blisko +/* + VelDesired = Global::Min0RSpeed( VelDesired, VelNext ); +*/ + if( VelNext == 0.0 ) { + VelDesired = VelNext; + } + else { + if( vel <= VelNext + fVelPlus ) { + // jeśli niewielkie przekroczenie, ale ma jechać + AccDesired = std::max( 0.0, AccPreferred ); // to olej (zacznij luzować) + } + } + ReactionTime = 0.1; // i orientuj się szybciej + } + } + else { + // zatrzymany (vel==0.0) + if( VelNext > 0.0 ) { + // można jechać + AccDesired = AccPreferred; + } + else { + // jeśli daleko jechać nie można + if( ActualProximityDist > ( + mvOccupied->CategoryFlag & 2 ? + fMinProximityDist : // cars + fMaxProximityDist ) ) { // trains and others + // ale ma kawałek do sygnalizatora + if( AccPreferred > 0.0 ) { + // dociagnij do semafora; + AccDesired = AccPreferred; + } + else { + //stoj + VelDesired = 0.0; + } + } + else { + // VelNext=0 i stoi bliżej niż fMaxProximityDist + VelDesired = 0.0; + } + } + } + } + else { + // gdy jedzie wolniej niż potrzeba, albo nie ma przeszkód na drodze + // normalna jazda + AccDesired = ( + VelDesired != 0.0 ? + AccPreferred : + -0.01 ); + } + // koniec predkosci nastepnej + + if( vel > VelDesired ) { + // jesli jedzie za szybko do AKTUALNEGO + if( VelDesired == 0.0 ) { + // jesli stoj, to hamuj, ale i tak juz za pozno :) + AccDesired = std::min( AccDesired, -0.9 ); // hamuj solidnie + } + else { + // slow down, not full stop + if( vel > ( VelDesired + fVelPlus ) ) { + // hamuj tak średnio + AccDesired = std::min( AccDesired, -fBrake_a0[ 0 ] * 0.5 ); + } + else { + // o 5 km/h to olej (zacznij luzować) + AccDesired = std::min( + AccDesired, // but don't override decceleration for VelNext + std::max( 0.0, AccPreferred ) ); + } + } + } + // koniec predkosci aktualnej + + // last step sanity check, until the whole calculation is straightened out + AccDesired = std::min( AccDesired, AccPreferred ); + + + + if (AIControllFlag) { + // część wykonawcza tylko dla AI, dla człowieka jedynie napisy + + // zapobieganie poslizgowi u nas + if (mvControlling->SlippingWheels) { + + if (!mvControlling->DecScndCtrl(2)) // bocznik na zero + mvControlling->DecMainCtrl(1); +/* + if (mvOccupied->BrakeCtrlPos == + mvOccupied->BrakeCtrlPosNo) // jeśli ostatnia pozycja hamowania + //yB: ten warunek wyżej nie ma sensu + mvOccupied->DecBrakeLevel(); // to cofnij hamulec + DecBrake(); // to cofnij hamulec + else + mvControlling->AntiSlippingButton(); +*/ + DecBrake(); // to cofnij hamulec + mvControlling->AntiSlippingButton(); + ++iDriverFailCount; + mvControlling->SlippingWheels = false; // flaga już wykorzystana + } + if (iDriverFailCount > maxdriverfails) + { + Psyche = Easyman; + if (iDriverFailCount > maxdriverfails * 2) + SetDriverPsyche(); + } + + if( ( true == mvOccupied->RadioStopFlag ) // radio-stop + && ( mvOccupied->Vel > 0.0 ) ) { // and still moving + // if the radio-stop was issued don't waste effort trying to fight it + while( true == DecSpeed() ) { ; } // just throttle down... + return; // ...and don't touch any other controls + } + + if (mvControlling->ConvOvldFlag || + !mvControlling->Mains) // WS może wywalić z powodu błędu w drutach + { // wywalił bezpiecznik nadmiarowy przetwornicy + PrepareEngine(); // próba ponownego załączenia + } + // włączanie bezpiecznika + if ((mvControlling->EngineType == ElectricSeriesMotor) || + (mvControlling->TrainType & dt_EZT) || + (mvControlling->EngineType == DieselElectric)) + if (mvControlling->FuseFlag || Need_TryAgain) + { + Need_TryAgain = false; // true, jeśli druga pozycja w elektryku nie załapała + mvControlling->DecScndCtrl(2); // nastawnik bocznikowania na 0 + mvControlling->DecMainCtrl(2); // nastawnik jazdy na 0 + mvControlling->MainSwitch(true); // Ra: dodałem, bo EN57 stawały po wywaleniu + if (mvControlling->FuseOn()) { + ++iDriverFailCount; + if (iDriverFailCount > maxdriverfails) + Psyche = Easyman; + if (iDriverFailCount > maxdriverfails * 2) + SetDriverPsyche(); + } + } + // NOTE: as a stop-gap measure the routine is limited to trains only while car calculations seem off + if( mvControlling->CategoryFlag == 1 ) { + if( -AccDesired * BrakeAccFactor() < ( + ( ( fReady > 0.4 ) || ( VelNext > vel - 40.0 ) ) ? + fBrake_a0[ 0 ] * 0.8 : + -fAccThreshold ) + / braking_distance_multiplier( VelNext ) ) { + AccDesired = std::max( -0.06, AccDesired ); + } + else { + ReactionTime = 0.25; // i orientuj się szybciej, jeśli hamujesz + } + } + if (mvOccupied->BrakeSystem == Pneumatic) // napełnianie uderzeniowe + if (mvOccupied->BrakeHandle == FV4a) + { + if( mvOccupied->BrakeCtrlPos == -2 ) { + mvOccupied->BrakeLevelSet( 0 ); + } + if( ( mvOccupied->PipePress < 3.0 ) + && ( AccDesired > -0.03 ) ) { + mvOccupied->BrakeReleaser( 1 ); + } + if( ( mvOccupied->BrakeCtrlPos == 0 ) + && ( AbsAccS < 0.0 ) + && ( AccDesired > -0.03 ) ) { + + if( ( mvOccupied->EqvtPipePress < 4.95 ) + && ( fReady > 0.35 ) ) { // a reszta składu jest na to gotowa + + if( iDrivigFlags & moveOerlikons ) { + // napełnianie w Oerlikonie + mvOccupied->BrakeLevelSet( -1 ); + } + } + else if( Need_BrakeRelease ) { + Need_BrakeRelease = false; + mvOccupied->BrakeReleaser( 1 ); + } + } + + if( ( mvOccupied->BrakeCtrlPos < 0 ) + && ( mvOccupied->EqvtPipePress > ( + fReady < 0.25 ? + 5.1 : + 5.2 ) ) ) { + mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); + } + } +#if LOGVELOCITY + WriteLog("Dist=" + FloatToStrF(ActualProximityDist, ffFixed, 7, 1) + + ", VelDesired=" + FloatToStrF(VelDesired, ffFixed, 7, 1) + + ", AccDesired=" + FloatToStrF(AccDesired, ffFixed, 7, 3) + + ", VelSignal=" + AnsiString(VelSignal) + ", VelNext=" + + AnsiString(VelNext)); +#endif + if( ( vel < 10.0 ) + && ( AccDesired > 0.1 ) ) { + // Ra 2F1H: jeśli prędkość jest mała, a można przyspieszać, + // to nie ograniczać przyspieszenia do 0.5m/ss + // przy małych prędkościach może być trudno utrzymać + AccDesired = std::max( 0.9, AccDesired ); + } + // małe przyspieszenie + // Ra 2F1I: wyłączyć kiedyś to uśrednianie i przeanalizować skanowanie, czemu migocze + if (AccDesired > -0.05) // hamowania lepeiej nie uśredniać + AccDesired = fAccDesiredAv = + 0.2 * AccDesired + + 0.8 * fAccDesiredAv; // uśrednione, żeby ograniczyć migotanie + if( VelDesired == 0.0 ) { + // Ra 2F1J: jeszcze jedna prowizoryczna łatka + if( AccDesired >= -0.01 ) { + AccDesired = -0.01; + } + } + if( AccDesired >= 0.0 ) { + if( true == TestFlag( iDrivigFlags, movePress ) ) { + // wyluzuj lokomotywę - może być więcej! + mvOccupied->BrakeReleaser( 1 ); + } + else if( OrderList[ OrderPos ] != Disconnect ) { + // przy odłączaniu nie zwalniamy tu hamulca + if( ( fAccGravity * fAccGravity < 0.001 ? + true : + AccDesired > 0.0 ) ) { + // on slopes disengage the brakes only if you actually intend to accelerate + while( true == DecBrake() ) { ; } // jeśli przyspieszamy, to nie hamujemy + if( ( mvOccupied->BrakePress > 0.4 ) + && ( mvOccupied->Hamulec->GetCRP() > 4.9 ) ) { + // wyluzuj lokomotywę, to szybciej ruszymy + mvOccupied->BrakeReleaser( 1 ); + } + else { + if( mvOccupied->PipePress >= 3.0 ) { + // TODO: combine all releaser handling in single decision tree instead of having bits all over the place + mvOccupied->BrakeReleaser( 0 ); + } + } + } + } + } + // margines dla prędkości jest doliczany tylko jeśli oczekiwana prędkość jest większa od 5km/h + if( false == TestFlag( iDrivigFlags, movePress ) ) { + // jeśli nie dociskanie + if( AccDesired < -0.05 ) { + while( true == DecSpeed() ) { ; } // jeśli hamujemy, to nie przyspieszamy + } + else if( ( vel > VelDesired ) + || ( fAccGravity < -0.01 ? + AccDesired < 0.0 : + AbsAccS > AccDesired ) ) { + // jak za bardzo przyspiesza albo prędkość przekroczona + DecSpeed(); // pojedyncze cofnięcie pozycji, bo na zero to przesada + } + } + // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej + // zwiekszanie predkosci + // Ra 2F1H: jest konflikt histerezy pomiędzy nastawioną pozycją a uzyskiwanym + // przyspieszeniem - utrzymanie pozycji powoduje przekroczenie przyspieszenia + if( AbsAccS < AccDesired ) { + // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz... + if( vel < ( + VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1 + VelDesired : + VelDesired - fVelMinus ) ) { + // ...jeśli prędkość w kierunku czoła jest mniejsza od dozwolonej o margines + if( ( ActualProximityDist > ( + mvOccupied->CategoryFlag & 2 ? + fMinProximityDist : // cars are allowed to move within min proximity distance + fMaxProximityDist ) ? // other vehicle types keep wider margin + true : + vel < VelNext ) ) { + // to można przyspieszyć + IncSpeed(); + } + } + } + // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej + // zmniejszanie predkosci + if( mvOccupied->TrainType & dt_EZT ) { + // właściwie, to warunek powinien być na działający EP + // Ra: to dobrze hamuje EP w EZT + if( ( AccDesired <= fAccThreshold ) // jeśli hamować - u góry ustawia się hamowanie na fAccThreshold + && ( ( AbsAccS > AccDesired ) + || ( mvOccupied->BrakeCtrlPos < 0 ) ) ) { + // hamować bardziej, gdy aktualne opóźnienie hamowania mniejsze niż (AccDesired) + IncBrake(); + } + else if( OrderList[ OrderPos ] != Disconnect ) { + // przy odłączaniu nie zwalniamy tu hamulca + if( AbsAccS < AccDesired - 0.05 ) { + // jeśli opóźnienie większe od wymaganego (z histerezą) luzowanie, gdy za dużo + if( mvOccupied->BrakeCtrlPos >= 0 ) { + DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu + } + } + else if( mvOccupied->Handle->TimeEP ) { + if( mvOccupied->Handle->GetPos( bh_EPR ) - + mvOccupied->Handle->GetPos( bh_EPN ) < + 0.1 ) { + mvOccupied->SwitchEPBrake( 0 ); + } + else { + mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_EPN ) ); + } + } + } // order != disconnect + } // type & dt_ezt + else { + // a stara wersja w miarę dobrze działa na składy wagonowe + if( ( ( fAccGravity < -0.05 ) && ( vel < 0.0 ) ) + || ( ( AccDesired < fAccGravity - 0.1 ) && ( AbsAccS > AccDesired + fBrake_a1[ 0 ] ) ) ) { + // u góry ustawia się hamowanie na fAccThreshold + if( ( fBrakeTime < 0.0 ) + || ( AccDesired < fAccGravity - 0.5 ) + || ( mvOccupied->BrakeCtrlPos <= 0 ) ) { + // jeśli upłynął czas reakcji hamulca, chyba że nagłe albo luzował + if( true == IncBrake() ) { + fBrakeTime = + 3.0 + + 0.5 * ( ( + mvOccupied->BrakeDelayFlag > bdelay_G ? + mvOccupied->BrakeDelay[ 1 ] : + mvOccupied->BrakeDelay[ 3 ] ) + - 3.0 ); + // Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do zatrzymania jest mniejszy + fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1 + } + } + } + if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - fBrake_a1[0]*0.51)) { + // jak hamuje, to nie tykaj kranu za często + // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 + if( OrderList[ OrderPos ] != Disconnect ) { + // przy odłączaniu nie zwalniamy tu hamulca + DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu + } + fBrakeTime = ( + mvOccupied->BrakeDelayFlag > bdelay_G ? + mvOccupied->BrakeDelay[ 0 ] : + mvOccupied->BrakeDelay[ 2 ] ) + / 3.0; + fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1 + } + // stop-gap measure to ensure cars actually brake to stop even when above calculactions go awry + // instead of releasing the brakes and creeping into obstacle at 1-2 km/h + if( mvControlling->CategoryFlag == 2 ) { + if( ( VelDesired == 0.0 ) + && ( vel > VelDesired ) + && ( ActualProximityDist <= fMinProximityDist ) + && ( mvOccupied->LocalBrakePos == 0 ) ) { + IncBrake(); + } + } + } + // Mietek-end1 + SpeedSet(); // ciągla regulacja prędkości +#if LOGVELOCITY + WriteLog("BrakePos=" + AnsiString(mvOccupied->BrakeCtrlPos) + ", MainCtrl=" + + AnsiString(mvControlling->MainCtrlPos)); +#endif + } // if (AIControllFlag) + } // kierunek różny od zera + else + { // tutaj, gdy pojazd jest wyłączony + if (!AIControllFlag) // jeśli sterowanie jest w gestii użytkownika + if (mvOccupied->Battery) // czy użytkownik załączył baterię? + if (mvOccupied->ActiveDir) // czy ustawił kierunek + { // jeśli tak, to uruchomienie skanowania + CheckVehicles(); // sprawdzić skład + TableClear(); // resetowanie tabelki skanowania + PrepareEngine(); // uruchomienie + } + } + if (AIControllFlag) + { // odhamowywanie składu po zatrzymaniu i zabezpieczanie lokomotywy + if ((OrderList[OrderPos] & (Disconnect | Connect)) == + 0) // przy (p)odłączaniu nie zwalniamy tu hamulca + if ((mvOccupied->V == 0.0) && ((VelDesired == 0.0) || (AccDesired == 0.0))) + if (mvOccupied->BrakeCtrlPos == mvOccupied->Handle->GetPos(bh_RP)) + mvOccupied->IncLocalBrakeLevel(1); // dodatkowy na pozycję 1 + else + mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP)); + } + break; // rzeczy robione przy jezdzie + } // switch (OrderList[OrderPos]) } void TController::JumpToNextOrder() @@ -4973,14 +5079,6 @@ std::string TController::StopReasonText() //- rozpoznają tylko zerową prędkość (jako koniec toru i brak podstaw do dalszego skanowania) //---------------------------------------------------------------------------------------------------------------------- -/* //nie używane -double TController::Distance(vector3 &p1,vector3 &n,vector3 &p2) -{//Ra:obliczenie odległości punktu (p1) od płaszczyzny o wektorze normalnym (n) przechodzącej przez -(p2) - return n.x*(p1.x-p2.x)+n.y*(p1.y-p2.y)+n.z*(p1.z-p2.z); //ax1+by1+cz1+d, gdzie d=-(ax2+by2+cz2) -}; -*/ - bool TController::BackwardTrackBusy(TTrack *Track) { // najpierw sprawdzamy, czy na danym torze są pojazdy z innego składu if( false == Track->Dynamics.empty() ) { @@ -5004,8 +5102,7 @@ TEvent * TController::CheckTrackEventBackward(double fDirection, TTrack *Track) return NULL; }; -TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, - TTrack *Track, TEvent *&Event) +TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, TEvent *&Event) { // szukanie sygnalizatora w kierunku przeciwnym jazdy (eventu odczytu komórki pamięci) TTrack *pTrackChVel = Track; // tor ze zmianą prędkości TTrack *pTrackFrom; // odcinek poprzedni, do znajdywania końca dróg @@ -5048,10 +5145,11 @@ TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, } if (Track == pTrackFrom) Track = NULL; // koniec, tak jak dla torów - if (Track ? - (Track->VelocityGet() == 0.0) || (Track->iDamageFlag & 128) || - BackwardTrackBusy(Track) : - true) + if( ( Track ? + ( ( Track->VelocityGet() == 0.0 ) + || ( Track->iDamageFlag & 128 ) + || ( true == BackwardTrackBusy( Track ) ) ) : + true ) ) { // gdy dalej toru nie ma albo zerowa prędkość, albo uszkadza pojazd fDistance = s; return NULL; // zwraca NULL, że skanowanie nie dało sensownych rezultatów diff --git a/Driver.h b/Driver.h index 8cdcc74b..dfb24ec8 100644 --- a/Driver.h +++ b/Driver.h @@ -52,12 +52,10 @@ enum TMovementStatus moveGuardSignal = 0x8000, // sygnał od kierownika (minął czas postoju) moveVisibility = 0x10000, // jazda na widoczność po przejechaniu S1 na SBL moveDoorOpened = 0x20000, // drzwi zostały otwarte - doliczyć czas na zamknięcie - movePushPull = - 0x40000, // zmiana czoła przez zmianę kabiny - nie odczepiać przy zmianie kierunku + movePushPull = 0x40000, // zmiana czoła przez zmianę kabiny - nie odczepiać przy zmianie kierunku moveSemaphorFound = 0x80000, // na drodze skanowania został znaleziony semafor moveSemaphorWasElapsed = 0x100000, // minięty został semafor - moveTrainInsideStation = - 0x200000, // pociąg między semaforem a rozjazdami lub następnym semaforem + moveTrainInsideStation = 0x200000, // pociąg między semaforem a rozjazdami lub następnym semaforem moveSpeedLimitFound = 0x400000 // pociąg w ograniczeniu z podaną jego długością }; @@ -147,10 +145,14 @@ class TSpeedPos public: TSpeedPos(TTrack *track, double dist, int flag); TSpeedPos(TEvent *event, double dist, TOrders order); - TSpeedPos() {}; + TSpeedPos() = default; void Clear(); bool Update(); - void UpdateDistance(double dist); + // aktualizuje odległość we wpisie + inline + void + UpdateDistance( double dist ) { + fDist -= dist; } bool Set(TEvent *e, double d, TOrders order = Wait_for_orders); void Set(TTrack *t, double d, int f); std::string TableText(); @@ -166,6 +168,7 @@ static const bool Humandriver = false; static const int maxorders = 32; // ilość rozkazów w tabelce static const int maxdriverfails = 4; // ile błędów może zrobić AI zanim zmieni nastawienie extern bool WriteLogFlag; // logowanie parametrów fizycznych +static const int BrakeAccTableSize = 20; //---------------------------------------------------------------------------- class TController @@ -201,8 +204,14 @@ private: // parametry aktualnego składu double fDriverBraking = 0.0; // po pomnożeniu przez v^2 [km/h] daje ~drogę hamowania [m] double fDriverDist = 0.0; // dopuszczalna odległość podjechania do przeszkody double fVelMax = -1.0; // maksymalna prędkość składu (sprawdzany każdy pojazd) + public: double fBrakeDist = 0.0; // przybliżona droga hamowania + double BrakeAccFactor(); + double fBrakeReaction = 1.0; //opóźnienie zadziałania hamulca - czas w s / (km/h) double fAccThreshold = 0.0; // próg opóźnienia dla zadziałania hamulca + double AbsAccS_pub = 0.0; // próg opóźnienia dla zadziałania hamulca + double fBrake_a0[BrakeAccTableSize+1] = { 0.0 }; // próg opóźnienia dla zadziałania hamulca + double fBrake_a1[BrakeAccTableSize+1] = { 0.0 }; // próg opóźnienia dla zadziałania hamulca public: double fLastStopExpDist = -1.0; // odległość wygasania ostateniego przystanku double ReactionTime = 0.0; // czas reakcji Ra: czego i na co? świadomości AI @@ -217,7 +226,6 @@ private: // parametry aktualnego składu double fActionTime = 0.0; // czas używany przy regulacji prędkości i zamykaniu drzwi double m_radiocontroltime{ 0.0 }; // timer used to control speed of radio operations TAction eAction = actSleep; // aktualny stan - bool HelpMeFlag = false; // wystawiane True jesli cos niedobrego sie dzieje public: inline TAction GetAction() { @@ -270,8 +278,6 @@ private: // parametry aktualnego składu OrderTop = 0; // rozkaz aktualny oraz wolne miejsce do wstawiania nowych std::ofstream LogFile; // zapis parametrow fizycznych std::ofstream AILogFile; // log AI - bool MaxVelFlag = false; - bool MinVelFlag = false; // Ra: to nie jest używane int iDirection = 0; // kierunek jazdy względem sprzęgów pojazdu, w którym siedzi AI (1=przód,-1=tył) int iDirectionOrder = 0; //żadany kierunek jazdy (służy do zmiany kierunku) int iVehicleCount = -2; // wartość neutralna // ilość pojazdów do odłączenia albo zabrania ze składu (-1=wszystkie) @@ -312,6 +318,7 @@ private: // parametry aktualnego składu void Activation(); // umieszczenie obsady w odpowiednim członie void ControllingSet(); // znajduje człon do sterowania void AutoRewident(); // ustawia hamulce w składzie + double ESMVelocity(bool Main); public: Mtable::TTrainParameters *Timetable() { @@ -321,7 +328,7 @@ private: // parametry aktualnego składu const TLocation &NewLocation, TStopReason reason = stopComm); bool PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason = stopComm); - bool UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę + void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę // procedury dotyczace rozkazow dla maszynisty void SetVelocity(double NewVel, double NewVelNext, TStopReason r = stopNone); // uaktualnia informacje o prędkości @@ -367,9 +374,12 @@ private: // parametry aktualnego składu bool TableAddNew(); bool TableNotFound(TEvent const *Event) const; // TEvent *TableCheckTrackEvent(double fDirection, TTrack *Track); - void TableTraceRoute(double fDistance, TDynamicObject *pVehicle = NULL); + void TableTraceRoute(double fDistance, TDynamicObject *pVehicle = nullptr); void TableCheck(double fDistance); TCommandType TableUpdate(double &fVelDes, double &fDist, double &fNext, double &fAcc); + // modifies brake distance for low target speeds, to ease braking rate in such situations + float + braking_distance_multiplier( float const Targetvelocity ); void TablePurger(); void TableSort(); inline double MoveDistanceGet() @@ -385,7 +395,6 @@ private: // parametry aktualnego składu { dMoveLen += distance * iDirection; //jak jedzie do tyłu to trzeba uwzględniać, że distance jest ujemna } - public: std::size_t TableSize() const { return sSpeedTable.size(); } void TableClear(); int TableDirection() { return iTableDirection; } @@ -409,12 +418,11 @@ private: // parametry aktualnego składu int StationCount(); int StationIndex(); bool IsStop(); - bool Primary() - { - return this ? ((iDrivigFlags & movePrimary) != 0) : false; - }; - int inline DrivigFlags() - { + inline + bool Primary() const { + return ( ( iDrivigFlags & movePrimary ) != 0 ); }; + inline + int DrivigFlags() const { return iDrivigFlags; }; void MoveTo(TDynamicObject *to); diff --git a/DynObj.cpp b/DynObj.cpp index b7bbf4f6..f2926f88 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1126,128 +1126,96 @@ void TDynamicObject::ABuCheckMyTrack() } // Ra: w poniższej funkcji jest problem ze sprzęgami -TDynamicObject * TDynamicObject::ABuFindObject(TTrack *Track, int ScanDir, - BYTE &CouplFound, double &dist) +TDynamicObject * +TDynamicObject::ABuFindObject( int &Foundcoupler, double &Distance, TTrack *Track, int const Direction, int const Mycoupler ) { // Zwraca wskaźnik najbliższego obiektu znajdującego się // na torze w określonym kierunku, ale tylko wtedy, kiedy // obiekty mogą się zderzyć, tzn. nie mijają się. + // WE: + // Track - tor, na ktorym odbywa sie poszukiwanie, + // Direction - kierunek szukania na torze (+1:w stronę Point2, -1:w stronę Point1) + // Mycoupler - nr sprzegu obiektu szukajacego; + // WY: + // wskaznik do znalezionego obiektu. + // Foundcoupler - nr sprzegu znalezionego obiektu + // Distance - distance to found object - // WE: Track - tor, na ktorym odbywa sie poszukiwanie, - // MyPointer - wskaznik do obiektu szukajacego. //Ra: zamieniłem na "this" - // ScanDir - kierunek szukania na torze (+1:w stronę Point2, -1:w stronę - // Point1) - // MyScanDir - kierunek szukania obiektu szukajacego (na jego torze); Ra: - // nie potrzebne - // MyCouplFound - nr sprzegu obiektu szukajacego; Ra: nie potrzebne - - // WY: wskaznik do znalezionego obiektu. - // CouplFound - nr sprzegu znalezionego obiektu - if( false == Track->Dynamics.empty() ) - { // sens szukania na tym torze jest tylko, gdy są na nim pojazdy - double MyTranslation; // pozycja szukającego na torze - double MinDist = Track->Length(); // najmniejsza znaleziona odleglość - // (zaczynamy od długości toru) - double TestDist; // robocza odległość od kolejnych pojazdów na danym odcinku - TDynamicObject *collider = nullptr; - // if (Track->iNumDynamics>1) - // iMinDist+=0; //tymczasowo pułapka - if (MyTrack == Track) // gdy szukanie na tym samym torze - MyTranslation = RaTranslationGet(); // położenie wózka względem Point1 toru - else // gdy szukanie na innym torze - if (ScanDir > 0) - MyTranslation = 0; // szukanie w kierunku Point2 (od zera) - jesteśmy w Point1 - else - MyTranslation = MinDist; // szukanie w kierunku Point1 (do zera) - jesteśmy w Point2 - if (ScanDir >= 0) - { // jeśli szukanie w kierunku Point2 - for( auto dynamic : Track->Dynamics ) { - // pętla po pojazdach - if( dynamic == this ) { - // szukający się nie liczy - continue; - } - - TestDist = ( dynamic->RaTranslationGet() ) - MyTranslation; // odległogłość tamtego od szukającego - if( ( TestDist > 0 ) && ( TestDist <= MinDist ) ) { // gdy jest po właściwej stronie i bliżej - // niż jakiś wcześniejszy - CouplFound = ( dynamic->RaDirectionGet() > 0 ) ? 1 : 0; // to, bo (ScanDir>=0) - if( Track->iCategoryFlag & 254 ) { - // trajektoria innego typu niż tor kolejowy - // dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po jednej szynie i się mija - // Ra: mijanie samochodów wcale nie jest proste - // Przesuniecie wzgledne pojazdow. Wyznaczane, zeby sprawdzic, - // czy pojazdy faktycznie sie zderzaja (moga byc przesuniete - // w/m siebie tak, ze nie zachodza na siebie i wtedy sie mijaja). - double RelOffsetH; // wzajemna odległość poprzeczna - if( CouplFound ) { - // my na tym torze byśmy byli w kierunku Point2 - // dla CouplFound=1 są zwroty zgodne - istotna różnica przesunięć - RelOffsetH = ( MoverParameters->OffsetTrackH - dynamic->MoverParameters->OffsetTrackH ); - } - else { - // dla CouplFound=0 są zwroty przeciwne - przesunięcia sumują się - RelOffsetH = ( MoverParameters->OffsetTrackH + dynamic->MoverParameters->OffsetTrackH ); - } - if( RelOffsetH < 0 ) { - RelOffsetH = -RelOffsetH; - } - if( RelOffsetH + RelOffsetH > MoverParameters->Dim.W + dynamic->MoverParameters->Dim.W ) { - // odległość większa od połowy sumy szerokości - kolizji nie będzie - continue; - } - // jeśli zahaczenie jest niewielkie, a jest miejsce na poboczu, to - // zjechać na pobocze - } - collider = dynamic; // potencjalna kolizja - MinDist = TestDist; // odleglość pomiędzy aktywnymi osiami pojazdów - } - - } - } - else //(ScanDir<0) - { - for( auto dynamic : Track->Dynamics ) { - - if( dynamic == this ) { continue; } - - TestDist = MyTranslation - ( dynamic->RaTranslationGet() ); //???-przesunięcie wózka względem Point1 toru - if( ( TestDist > 0 ) && ( TestDist < MinDist ) ) { - CouplFound = ( dynamic->RaDirectionGet() > 0 ) ? 0 : 1; // odwrotnie, bo (ScanDir<0) - if( Track->iCategoryFlag & 254 ) // trajektoria innego typu niż tor kolejowy - { // dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po jednej szynie i się mija - // Ra: mijanie samochodów wcale nie jest proste - // Przesunięcie względne pojazdów. Wyznaczane, żeby sprawdzić, - // czy pojazdy faktycznie się zderzają (mogą być przesunięte - // w/m siebie tak, że nie zachodzą na siebie i wtedy sie mijają). - double RelOffsetH; // wzajemna odległość poprzeczna - if( CouplFound ) { - // my na tym torze byśmy byli w kierunku Point1 - // dla CouplFound=1 są zwroty zgodne - istotna różnica przesunięć - RelOffsetH = ( MoverParameters->OffsetTrackH - dynamic->MoverParameters->OffsetTrackH ); - } - else { - // dla CouplFound=0 są zwroty przeciwne - przesunięcia sumują się - RelOffsetH = ( MoverParameters->OffsetTrackH + dynamic->MoverParameters->OffsetTrackH ); - } - if( RelOffsetH < 0 ) { - RelOffsetH = -RelOffsetH; - } - if( RelOffsetH + RelOffsetH > MoverParameters->Dim.W + dynamic->MoverParameters->Dim.W ) { - // odległość większa od połowy sumy szerokości - kolizji nie będzie - continue; - } - } - collider = dynamic; // potencjalna kolizja - MinDist = TestDist; // odleglość pomiędzy aktywnymi osiami pojazdów - } - } - } - dist += MinDist; // doliczenie odległości przeszkody albo długości odcinka do przeskanowanej odległości - return collider; + if( true == Track->Dynamics.empty() ) { + // sens szukania na tym torze jest tylko, gdy są na nim pojazdy + Distance += Track->Length(); // doliczenie długości odcinka do przeskanowanej odległości + return nullptr; // nie ma pojazdów na torze, to jest NULL } - dist += Track->Length(); // doliczenie długości odcinka do przeskanowanej - // odległości - return nullptr; // nie ma pojazdów na torze, to jest NULL + + double distance = Track->Length(); // najmniejsza znaleziona odleglość (zaczynamy od długości toru) + double myposition; // pozycja szukającego na torze + TDynamicObject *foundobject = nullptr; + if( MyTrack == Track ) { + // gdy szukanie na tym samym torze + myposition = RaTranslationGet(); // położenie wózka względem Point1 toru + } + else { + // gdy szukanie na innym torze + if( Direction > 0 ) { + // szukanie w kierunku Point2 (od zera) - jesteśmy w Point1 + myposition = 0; + } + else { + // szukanie w kierunku Point1 (do zera) - jesteśmy w Point2 + myposition = distance; + } + } + + double objectposition; // robocza odległość od kolejnych pojazdów na danym odcinku + for( auto dynamic : Track->Dynamics ) { + + if( dynamic == this ) { continue; } // szukający się nie liczy + + if( Direction > 0 ) { + // jeśli szukanie w kierunku Point2 + objectposition = ( dynamic->RaTranslationGet() ) - myposition; // odległogłość tamtego od szukającego + if( ( objectposition > 0 ) + && ( objectposition < distance ) ) { + // gdy jest po właściwej stronie i bliżej niż jakiś wcześniejszy + Foundcoupler = ( dynamic->RaDirectionGet() > 0 ) ? 1 : 0; // to, bo (ScanDir>=0) + } + else { continue; } + } + else { + objectposition = myposition - ( dynamic->RaTranslationGet() ); //???-przesunięcie wózka względem Point1 toru + if( ( objectposition > 0 ) + && ( objectposition < distance ) ) { + Foundcoupler = ( dynamic->RaDirectionGet() > 0 ) ? 0 : 1; // odwrotnie, bo (ScanDir<0) + } + else { continue; } + } + + if( Track->iCategoryFlag & 254 ) { + // trajektoria innego typu niż tor kolejowy + // dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po jednej szynie i się mija + // Ra: mijanie samochodów wcale nie jest proste + // Przesuniecie wzgledne pojazdow. Wyznaczane, zeby sprawdzic, + // czy pojazdy faktycznie sie zderzaja (moga byc przesuniete + // w/m siebie tak, ze nie zachodza na siebie i wtedy sie mijaja). + double relativeoffset; // wzajemna odległość poprzeczna + if( Foundcoupler != Mycoupler ) { + // facing the same direction + relativeoffset = std::abs( MoverParameters->OffsetTrackH - dynamic->MoverParameters->OffsetTrackH ); + } + else { + relativeoffset = std::abs( MoverParameters->OffsetTrackH + dynamic->MoverParameters->OffsetTrackH ); + } + if( relativeoffset + relativeoffset > MoverParameters->Dim.W + dynamic->MoverParameters->Dim.W ) { + // odległość większa od połowy sumy szerokości - kolizji nie będzie + continue; + } + // jeśli zahaczenie jest niewielkie, a jest miejsce na poboczu, to zjechać na pobocze + } + foundobject = dynamic; // potencjalna kolizja + distance = objectposition; // odleglość pomiędzy aktywnymi osiami pojazdów + } + + Distance += distance; // doliczenie odległości przeszkody albo długości odcinka do przeskanowanej odległości + return foundobject; } int TDynamicObject::DettachStatus(int dir) @@ -1294,9 +1262,9 @@ void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir) { if (PrevConnected) // pojazd od strony sprzęgu 0 { - if (MoverParameters->Couplers[0].CoupleDist > - MinDist) // sprzęgi wirtualne zawsze przekraczają - { + if (MoverParameters->Couplers[0].CoupleDist > MinDist) { + // sprzęgi wirtualne zawsze przekraczają + if ((PrevConnectedNo ? PrevConnected->NextConnected : PrevConnected->PrevConnected) == this) { // Ra: nie rozłączamy znalezionego, jeżeli nie do nas @@ -1314,10 +1282,11 @@ void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir) PrevConnected->NextConnected = NULL; } } + // za to zawsze odłączamy siebie PrevConnected = NULL; PrevConnectedNo = 2; // sprzęg 0 nie podłączony - MoverParameters->Couplers[0].Connected = NULL; + MoverParameters->Couplers[0].Connected = nullptr; } } } @@ -1325,9 +1294,9 @@ void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir) { if (NextConnected) // pojazd od strony sprzęgu 1 { - if (MoverParameters->Couplers[1].CoupleDist > - MinDist) // sprzęgi wirtualne zawsze przekraczają - { + if (MoverParameters->Couplers[1].CoupleDist > MinDist) { + // sprzęgi wirtualne zawsze przekraczają + if ((NextConnectedNo ? NextConnected->NextConnected : NextConnected->PrevConnected) == this) { // Ra: nie rozłączamy znalezionego, jeżeli nie do nas @@ -1345,180 +1314,147 @@ void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir) NextConnected->NextConnected = NULL; } } + NextConnected = NULL; NextConnectedNo = 2; // sprzęg 1 nie podłączony - MoverParameters->Couplers[1].Connected = NULL; + MoverParameters->Couplers[1].Connected = nullptr; } } } } -void TDynamicObject::ABuScanObjects(int ScanDir, double ScanDist) +void TDynamicObject::ABuScanObjects( int Direction, double Distance ) { // skanowanie toru w poszukiwaniu kolidujących pojazdów // ScanDir - określa kierunek poszukiwania zależnie od zwrotu prędkości // pojazdu // ScanDir=1 - od strony Coupler0, ScanDir=-1 - od strony Coupler1 - int MyScanDir = ScanDir; // zapamiętanie kierunku poszukiwań na torze - // początkowym, względem sprzęgów - TTrackFollower *FirstAxle = (MyScanDir > 0 ? &Axle0 : &Axle1); // można by to trzymać w trainset - TTrack *Track = FirstAxle->GetTrack(); // tor na którym "stoi" skrajny wózek + int initialdirection = Direction; // zapamiętanie kierunku poszukiwań na torze początkowym, względem sprzęgów + TTrackFollower *firstaxle = (initialdirection > 0 ? &Axle0 : &Axle1); // można by to trzymać w trainset + TTrack *track = firstaxle->GetTrack(); // tor na którym "stoi" skrajny wózek // (może być inny niż tor pojazdu) - if (FirstAxle->GetDirection() < 0) // czy oś jest ustawiona w stronę Point1? - ScanDir = -ScanDir; // jeśli tak, to kierunek szukania będzie przeciwny + if( firstaxle->GetDirection() < 0 ) { + // czy oś jest ustawiona w stronę Point1? + Direction = -Direction; // jeśli tak, to kierunek szukania będzie przeciwny + } // (teraz względem // toru) - BYTE MyCouplFound; // numer sprzęgu do podłączenia w obiekcie szukajacym - MyCouplFound = (MyScanDir < 0) ? 1 : 0; - BYTE CouplFound; // numer sprzęgu w znalezionym obiekcie (znaleziony wypełni) - TDynamicObject *FoundedObj; // znaleziony obiekt - double ActDist = 0; // przeskanowana odleglość; odległość do zawalidrogi - FoundedObj = ABuFindObject(Track, ScanDir, CouplFound, - ActDist); // zaczynamy szukać na tym samym torze + int const mycoupler = ( initialdirection < 0 ? 1 : 0 ); // numer sprzęgu do podłączenia w obiekcie szukajacym + int foundcoupler { -1 }; // numer sprzęgu w znalezionym obiekcie (znaleziony wypełni) + double distance = 0; // przeskanowana odleglość; odległość do zawalidrogi + TDynamicObject *foundobject = ABuFindObject( foundcoupler, distance, track, Direction, mycoupler ); // zaczynamy szukać na tym samym torze - /* - if (FoundedObj) //jak coś znajdzie, to śledzimy - {//powtórzenie wyszukiwania tylko do zastawiania pułepek podczas testów - if (ABuGetDirection()<0) ScanDir=ScanDir; //ustalenie kierunku względem toru - FoundedObj=ABuFindObject(Track,this,ScanDir,CouplFound); - } - */ - - if (DebugModeFlag) - if (FoundedObj) // kod służący do logowania błędów - if (CouplFound == 0) - { - if (FoundedObj->PrevConnected) - if (FoundedObj->PrevConnected != this) // odświeżenie tego samego się nie liczy - WriteLog("0! Coupler warning on " + asName + ":" + - to_string(MyCouplFound) + " - " + FoundedObj->asName + - ":0 connected to " + FoundedObj->PrevConnected->asName + ":" + - to_string(FoundedObj->PrevConnectedNo)); - } - else - { - if (FoundedObj->NextConnected) - if (FoundedObj->NextConnected != this) // odświeżenie tego samego się nie liczy - WriteLog("0! Coupler warning on " + asName + ":" + - to_string(MyCouplFound) + " - " + FoundedObj->asName + - ":1 connected to " + FoundedObj->NextConnected->asName + ":" + - to_string(FoundedObj->NextConnectedNo)); - } - - if (FoundedObj == NULL) // jeśli nie ma na tym samym, szukamy po okolicy - { // szukanie najblizszego toru z jakims obiektem + if( foundobject == nullptr ) { + // jeśli nie ma na tym samym, szukamy po okolicy szukanie najblizszego toru z jakims obiektem // praktycznie przeklejone z TraceRoute()... - // double CurrDist=0; //aktualna dlugosc toru - if (ScanDir >= 0) // uwzględniamy kawalek przeanalizowanego wcześniej toru - ActDist = Track->Length() - FirstAxle->GetTranslation(); // odległość osi od Point2 toru + if (Direction >= 0) // uwzględniamy kawalek przeanalizowanego wcześniej toru + distance = track->Length() - firstaxle->GetTranslation(); // odległość osi od Point2 toru else - ActDist = FirstAxle->GetTranslation(); // odległość osi od Point1 toru - while (ActDist < ScanDist) - { - // ActDist+=CurrDist; //odległość już przeanalizowana - if (ScanDir > 0) // w kierunku Point2 toru - { - if (Track ? Track->iNextDirection : - false) // jeśli następny tor jest podpięty od Point2 - ScanDir = -ScanDir; // to zmieniamy kierunek szukania na tym torze - Track = Track->CurrentNext(); // potem dopiero zmieniamy wskaźnik + distance = firstaxle->GetTranslation(); // odległość osi od Point1 toru + + while (distance < Distance) { + if (Direction > 0) { + // w kierunku Point2 toru + if( track ? + track->iNextDirection : + false ) { + // jeśli następny tor jest podpięty od Point2 + Direction = -Direction; // to zmieniamy kierunek szukania na tym torze + } + track = track->CurrentNext(); // potem dopiero zmieniamy wskaźnik } - else // w kierunku Point1 - { - if (Track ? !Track->iPrevDirection : - true) // jeśli poprzedni tor nie jest podpięty od Point2 - ScanDir = -ScanDir; // to zmieniamy kierunek szukania na tym torze - Track = Track->CurrentPrev(); // potem dopiero zmieniamy wskaźnik + else { + // w kierunku Point1 + if( track ? + !track->iPrevDirection : + true ) { + // jeśli poprzedni tor nie jest podpięty od Point2 + Direction = -Direction; // to zmieniamy kierunek szukania na tym torze + } + track = track->CurrentPrev(); // potem dopiero zmieniamy wskaźnik } - if (Track) - { // jesli jest kolejny odcinek toru - // CurrDist=Track->Length(); //doliczenie tego toru do przejrzanego - // dystandu - FoundedObj = ABuFindObject(Track, ScanDir, CouplFound, - ActDist); // przejrzenie pojazdów tego toru - if (FoundedObj) - { - // ActDist=ScanDist; //wyjście z pętli poszukiwania + if (track) { + // jesli jest kolejny odcinek toru + foundobject = ABuFindObject(foundcoupler, distance, track, Direction, mycoupler); // przejrzenie pojazdów tego toru + if (foundobject) { break; } } - else // jeśli toru nie ma, to wychodzimy - { - ActDist = ScanDist + 1.0; // koniec przeglądania torów + else { + // jeśli toru nie ma, to wychodzimy + distance = Distance + 1.0; // koniec przeglądania torów break; } } } // Koniec szukania najbliższego toru z jakimś obiektem. + // teraz odczepianie i jeśli coś się znalazło, doczepianie. - if (MyScanDir > 0 ? PrevConnected : NextConnected) - if ((MyScanDir > 0 ? PrevConnected : NextConnected) != FoundedObj) - CouplersDettach(1.0, MyScanDir); // odłączamy, jeśli dalej niż metr - // i łączenie sprzęgiem wirtualnym - if (FoundedObj) - { // siebie można bezpiecznie podłączyć jednostronnie do - // znalezionego - MoverParameters->Attach(MyCouplFound, CouplFound, FoundedObj->MoverParameters, - ctrain_virtual); - // MoverParameters->Couplers[MyCouplFound].Render=false; //wirtualnego nie - // renderujemy - if (MyCouplFound == 0) - { - PrevConnected = FoundedObj; // pojazd od strony sprzęgu 0 - PrevConnectedNo = CouplFound; + auto connectedobject = ( + initialdirection > 0 ? + PrevConnected : + NextConnected ); + if( ( connectedobject != nullptr ) + && ( connectedobject != foundobject ) ) { + // odłączamy, jeśli dalej niż metr i łączenie sprzęgiem wirtualnym + CouplersDettach( 1.0, initialdirection ); + } + + if (foundobject) { + // siebie można bezpiecznie podłączyć jednostronnie do znalezionego + MoverParameters->Attach( mycoupler, foundcoupler, foundobject->MoverParameters, coupling::faux ); + // MoverParameters->Couplers[MyCouplFound].Render=false; //wirtualnego nie renderujemy + if( mycoupler == TMoverParameters::side::front ) { + PrevConnected = foundobject; // pojazd od strony sprzęgu 0 + PrevConnectedNo = foundcoupler; } - else - { - NextConnected = FoundedObj; // pojazd od strony sprzęgu 1 - NextConnectedNo = CouplFound; + else { + NextConnected = foundobject; // pojazd od strony sprzęgu 1 + NextConnectedNo = foundcoupler; } - if (FoundedObj->MoverParameters->Couplers[CouplFound].CouplingFlag == ctrain_virtual) - { // Ra: wpinamy się wirtualnym tylko jeśli znaleziony - // ma wirtualny sprzęg - FoundedObj->MoverParameters->Attach(CouplFound, MyCouplFound, this->MoverParameters, - ctrain_virtual); - if (CouplFound == 0) // jeśli widoczny sprzęg 0 znalezionego - { - if (DebugModeFlag) - if (FoundedObj->PrevConnected) - if (FoundedObj->PrevConnected != this) - WriteLog("1! Coupler warning on " + asName + ":" + - to_string(MyCouplFound) + " - " + FoundedObj->asName + - ":0 connected to " + FoundedObj->PrevConnected->asName + ":" + - to_string(FoundedObj->PrevConnectedNo)); - FoundedObj->PrevConnected = this; - FoundedObj->PrevConnectedNo = MyCouplFound; + + if( foundobject->MoverParameters->Couplers[ foundcoupler ].CouplingFlag == coupling::faux ) { + // Ra: wpinamy się wirtualnym tylko jeśli znaleziony ma wirtualny sprzęg + foundobject->MoverParameters->Attach( foundcoupler, mycoupler, this->MoverParameters, coupling::faux ); + + if( foundcoupler == TMoverParameters::side::front ) { + // jeśli widoczny sprzęg 0 znalezionego + if( ( DebugModeFlag ) + && ( foundobject->PrevConnected ) + && ( foundobject->PrevConnected != this ) ) { + WriteLog( "ScanObjects(): formed virtual link between \"" + asName + "\" (coupler " + to_string( mycoupler ) + ") and \"" + foundobject->asName + "\" (coupler " + to_string( foundcoupler ) + ")" ); + } + foundobject->PrevConnected = this; + foundobject->PrevConnectedNo = mycoupler; } - else // jeśli widoczny sprzęg 1 znalezionego - { - if (DebugModeFlag) - if (FoundedObj->NextConnected) - if (FoundedObj->NextConnected != this) - WriteLog("1! Coupler warning on " + asName + ":" + - to_string(MyCouplFound) + " - " + FoundedObj->asName + - ":1 connected to " + FoundedObj->NextConnected->asName + ":" + - to_string(FoundedObj->NextConnectedNo)); - FoundedObj->NextConnected = this; - FoundedObj->NextConnectedNo = MyCouplFound; + else { + // jeśli widoczny sprzęg 1 znalezionego + if( ( DebugModeFlag ) + && ( foundobject->NextConnected ) + && ( foundobject->NextConnected != this ) ) { + WriteLog( "ScanObjects(): formed virtual link between \"" + asName + "\" (coupler " + to_string( mycoupler ) + ") and \"" + foundobject->asName + "\" (coupler " + to_string( foundcoupler ) + ")" ); + } + foundobject->NextConnected = this; + foundobject->NextConnectedNo = mycoupler; } } - // Ra: jeśli dwa samochody się mijają na odcinku przed zawrotką, to - // odległość między nimi - // nie może być liczona w linii prostej! - fTrackBlock = MoverParameters->Couplers[MyCouplFound] - .CoupleDist; // odległość do najbliższego pojazdu w linii prostej - if (Track->iCategoryFlag > 1) // jeśli samochód - if (ActDist > MoverParameters->Dim.L + - FoundedObj->MoverParameters->Dim - .L) // przeskanowana odległość większa od długości pojazdów + + // odległość do najbliższego pojazdu w linii prostej + // Ra: jeśli dwa samochody się mijają na odcinku przed zawrotką, to odległość między nimi nie może być liczona w linii prostej! + fTrackBlock = MoverParameters->Couplers[mycoupler].CoupleDist; + if( track->iCategoryFlag & 254 ) { + // jeśli samochód + if( distance > MoverParameters->Dim.L + foundobject->MoverParameters->Dim.L ) { + // przeskanowana odległość większa od długości pojazdów // else if (ActDistasName); + fTrackBlock = distance; // ta odległość jest wiecej warta + } + } } - else // nic nie znalezione, to nie ma przeszkód + else { + // nic nie znalezione, to nie ma przeszkód fTrackBlock = 10000.0; + } } //----------ABu: koniec skanowania pojazdow @@ -1540,7 +1476,6 @@ TDynamicObject::TDynamicObject() bBrakeAcc = false; NextConnected = PrevConnected = NULL; NextConnectedNo = PrevConnectedNo = 2; // ABu: Numery sprzegow. 2=nie podłączony - CouplCounter = 50; // będzie sprawdzać na początku asName = ""; bEnabled = true; MyTrack = NULL; @@ -1880,14 +1815,20 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" if (MoverParameters->CategoryFlag & 2) // jeśli samochód { // ustawianie samochodow na poboczu albo na środku drogi - if (Track->fTrackWidth < 3.5) // jeśli droga wąska + if( Track->fTrackWidth < 3.5 ) // jeśli droga wąska MoverParameters->OffsetTrackH = 0.0; // to stawiamy na środku, niezależnie od stanu // ruchu - else if (driveractive) // od 3.5m do 8.0m jedzie po środku pasa, dla + else if( driveractive ) {// od 3.5m do 8.0m jedzie po środku pasa, dla // szerszych w odległości // 1.5m - MoverParameters->OffsetTrackH = - Track->fTrackWidth <= 8.0 ? -Track->fTrackWidth * 0.25 : -1.5; + auto const lanefreespace = 0.5 * Track->fTrackWidth - MoverParameters->Dim.W; + MoverParameters->OffsetTrackH = ( + lanefreespace > 1.5 ? + -0.5 * MoverParameters->Dim.W - 0.75 : // wide road, keep near centre with safe margin + ( lanefreespace > 0.1 ? + -0.25 * Track->fTrackWidth : // narrow fit, stay on the middle + -0.5 * MoverParameters->Dim.W - 0.075 ) ); // too narrow, just keep some minimal clearance + } else // jak stoi, to kołem na poboczu i pobieramy szerokość razem z // poboczem, ale nie z // chodnikiem @@ -2004,14 +1945,14 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" compartmentindex < 10 ? "0" + std::to_string( compartmentindex ) : std::to_string( compartmentindex ) ); - submodel = mdLowPolyInt->GetFromName( compartmentname.c_str() ); + submodel = mdLowPolyInt->GetFromName( compartmentname ); if( submodel != nullptr ) { // if specified compartment was found we check also for potential matching section in the currently assigned load // NOTE: if the load gets changed this will invalidate stored pointers. TODO: rebuild the table on load change SectionLightLevels.emplace_back( submodel, ( mdLoad != nullptr ? - mdLoad->GetFromName( compartmentname.c_str() ): + mdLoad->GetFromName( compartmentname ): nullptr ), 0.0f ); } @@ -2024,11 +1965,11 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" for (int i = 0; i < 2; i++) { asAnimName = std::string("buffer_left0") + to_string(i + 1); - smBuforLewy[i] = mdModel->GetFromName(asAnimName.c_str()); + smBuforLewy[i] = mdModel->GetFromName(asAnimName); if (smBuforLewy[i]) smBuforLewy[i]->WillBeAnimated(); // ustawienie flagi animacji asAnimName = std::string("buffer_right0") + to_string(i + 1); - smBuforPrawy[i] = mdModel->GetFromName(asAnimName.c_str()); + smBuforPrawy[i] = mdModel->GetFromName(asAnimName); if (smBuforPrawy[i]) smBuforPrawy[i]->WillBeAnimated(); } @@ -2998,8 +2939,8 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->AccN *= -ABuGetDirection(); */ // if (dDOMoveLen!=0.0) //Ra: nie może być, bo blokuje Event0 - if (Mechanik) - Mechanik->MoveDistanceAdd(dDOMoveLen); // dodanie aktualnego przemieszczenia + if( Mechanik ) + Mechanik->MoveDistanceAdd( dDOMoveLen ); // dodanie aktualnego przemieszczenia Move(dDOMoveLen); if (!bEnabled) // usuwane pojazdy nie mają toru { // pojazd do usunięcia @@ -3473,45 +3414,6 @@ bool TDynamicObject::Update(double dt, double dt1) toggle_lights(); } - // ABu-160303 sledzenie toru przed obiektem: ******************************* - // Z obserwacji: v>0 -> Coupler 0; v<0 ->coupler1 (Ra: prędkość jest związana - // z pojazdem) - // Rozroznienie jest tutaj, zeby niepotrzebnie nie skakac do funkcji. Nie jest - // uzaleznione - // od obecnosci AI, zeby uwzglednic np. jadace bez lokomotywy wagony. - // Ra: można by przenieść na poziom obiektu reprezentującego skład, aby nie - // sprawdzać środkowych - if (CouplCounter > 25) // licznik, aby nie robić za każdym razem - { // poszukiwanie czegoś do zderzenia się - fTrackBlock = 10000.0; // na razie nie ma przeszkód (na wypadek nie - // uruchomienia skanowania) - // jeśli nie ma zwrotnicy po drodze, to tylko przeliczyć odległość? - if (MoverParameters->V > 0.03) //[m/s] jeśli jedzie do przodu (w kierunku Coupler 0) - { - if (MoverParameters->Couplers[0].CouplingFlag == - ctrain_virtual) // brak pojazdu podpiętego? - { - ABuScanObjects(1, fScanDist); // szukanie czegoś do podłączenia - // WriteLog(asName+" - block 0: "+AnsiString(fTrackBlock)); - } - } - else if (MoverParameters->V < -0.03) //[m/s] jeśli jedzie do tyłu (w kierunku Coupler 1) - if (MoverParameters->Couplers[1].CouplingFlag == - ctrain_virtual) // brak pojazdu podpiętego? - { - ABuScanObjects(-1, fScanDist); - // WriteLog(asName+" - block 1: "+AnsiString(fTrackBlock)); - } - CouplCounter = Random(20); // ponowne sprawdzenie po losowym czasie - } - if (MoverParameters->Vel > 0.1) //[km/h] - ++CouplCounter; // jazda sprzyja poszukiwaniu połączenia - else - { - CouplCounter = 25; // a bezruch nie, ale trzeba zaktualizować odległość, bo - // zawalidroga może - // sobie pojechać - } if (MoverParameters->DerailReason > 0) { switch (MoverParameters->DerailReason) @@ -4038,7 +3940,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, { std::string nowheretexture = TextureTest(Global::asCurrentTexturePath + "nowhere"); // na razie prymitywnie if( false == nowheretexture.empty() ) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( nowheretexture, "", 9 ); + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( nowheretexture ); } if (m_materialdata.multi_textures > 0) { @@ -4050,7 +3952,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, int skinindex = 0; std::string texturename; nameparser >> texturename; while( ( texturename != "" ) && ( skinindex < 4 ) ) { - m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + texturename, "" ); + m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + texturename ); ++skinindex; texturename = ""; nameparser >> texturename; } @@ -4060,24 +3962,24 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, // otherwise try the basic approach int skinindex = 0; do { - texture_handle texture = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), "", Global::iDynamicFiltering, true ); - if( texture == NULL ) { + material_handle material = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), true ); + if( material == null_handle ) { break; } - m_materialdata.replacable_skins[ skinindex + 1 ] = texture; + m_materialdata.replacable_skins[ skinindex + 1 ] = material; ++skinindex; } while( skinindex < 4 ); m_materialdata.multi_textures = skinindex; if( m_materialdata.multi_textures == 0 ) { // zestaw nie zadziałał, próbujemy normanie - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + ReplacableSkin ); } } } else { - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + ReplacableSkin ); } - if( GfxRenderer.Texture( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { + if( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { // tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha = 0x31310031; } @@ -4087,17 +3989,17 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, } if( ( m_materialdata.replacable_skins[ 2 ] ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 2 ] ).has_alpha ) ) { + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 2 ] ).has_alpha ) ) { // tekstura -2 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha |= 0x02020002; } if( ( m_materialdata.replacable_skins[ 3 ] ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 3 ] ).has_alpha ) ) { + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 3 ] ).has_alpha ) ) { // tekstura -3 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha |= 0x04040004; } if( ( m_materialdata.replacable_skins[ 4 ] ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 4 ] ).has_alpha ) ) { + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 4 ] ).has_alpha ) ) { // tekstura -4 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha |= 0x08080008; } @@ -4312,7 +4214,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for (int i = 0; i < iAnimType[ANIM_PANTS]; i++) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string(i + 1); - sm = mdModel->GetFromName(asAnimName.c_str()); + sm = mdModel->GetFromName(asAnimName); pants[i].smElement[0] = sm; // jak NULL, to nie będzie animowany if (sm) { // w EP09 wywalało się tu z powodu NULL @@ -4404,7 +4306,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string( i + 1 ); - sm = mdModel->GetFromName( asAnimName.c_str() ); + sm = mdModel->GetFromName( asAnimName ); pants[ i ].smElement[ 1 ] = sm; // jak NULL, to nie będzie animowany if( sm ) { // w EP09 wywalało się tu z powodu NULL sm->WillBeAnimated(); @@ -4438,7 +4340,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string( i + 1 ); - pants[ i ].smElement[ 2 ] = mdModel->GetFromName( asAnimName.c_str() ); + pants[ i ].smElement[ 2 ] = mdModel->GetFromName( asAnimName ); pants[ i ].smElement[ 2 ]->WillBeAnimated(); } } @@ -4451,7 +4353,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string( i + 1 ); - pants[ i ].smElement[ 3 ] = mdModel->GetFromName( asAnimName.c_str() ); + pants[ i ].smElement[ 3 ] = mdModel->GetFromName( asAnimName ); pants[ i ].smElement[ 3 ]->WillBeAnimated(); } } @@ -4464,7 +4366,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for( int i = 0; i < iAnimType[ ANIM_PANTS ]; i++ ) { // Winger 160204: wyszukiwanie max 2 patykow o nazwie str* asAnimName = token + std::to_string( i + 1 ); - pants[ i ].smElement[ 4 ] = mdModel->GetFromName( asAnimName.c_str() ); + pants[ i ].smElement[ 4 ] = mdModel->GetFromName( asAnimName ); pants[ i ].smElement[ 4 ]->WillBeAnimated(); /* pants[ i ].yUpdate = UpdatePant; */ pants[ i ].yUpdate = std::bind( &TDynamicObject::UpdatePant, this, std::placeholders::_1 ); @@ -4598,7 +4500,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, for (int i = 1; i <= 4; i++) { // McZapkie-050402: wyszukiwanie max 4 wahaczy o nazwie str* asAnimName = token + std::to_string(i); - smWahacze[i - 1] = mdModel->GetFromName(asAnimName.c_str()); + smWahacze[i - 1] = mdModel->GetFromName(asAnimName); smWahacze[i - 1]->WillBeAnimated(); } parser.getTokens(); parser >> token; @@ -4635,7 +4537,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* asAnimName = token + std::to_string(i + 1); pAnimations[i + j].smAnimated = - mdModel->GetFromName(asAnimName.c_str()); // ustalenie submodelu + mdModel->GetFromName(asAnimName); // ustalenie submodelu if (pAnimations[i + j].smAnimated) { //++iAnimatedDoors; pAnimations[i + j].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu @@ -5139,7 +5041,6 @@ void TDynamicObject::RaLightsSet(int head, int rear) int TDynamicObject::DirectionSet(int d) { // ustawienie kierunku w składzie (wykonuje AI) iDirection = d > 0 ? 1 : 0; // d:1=zgodny,-1=przeciwny; iDirection:1=zgodny,0=przeciwny; - CouplCounter = 20; //żeby normalnie skanować kolizje, to musi ruszyć z miejsca if (MyTrack) { // podczas wczytywania wstawiane jest AI, ale może jeszcze nie // być toru @@ -5373,7 +5274,7 @@ int TDynamicObject::RouteWish(TTrack *tr) std::string TDynamicObject::TextureTest(std::string const &name) { // Ra 2015-01: sprawdzenie dostępności tekstury o podanej nazwie - std::vector extensions = { ".dds", ".tga", ".bmp" }; + std::vector extensions = { ".mat", ".dds", ".tga", ".bmp" }; for( auto const &extension : extensions ) { if( true == FileExists( name + extension ) ) { return name + extension; @@ -5395,36 +5296,22 @@ void TDynamicObject::DestinationSet(std::string to, std::string numer) numer = Global::Bezogonkow(numer); asDestination = to; to = Global::Bezogonkow(to); // do szukania pliku obcinamy ogonki - std::string x = TextureTest(asBaseDir + numer + "@" + MoverParameters->TypeName); - if (!x.empty()) - { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - return; + + std::vector destinations = { + asBaseDir + numer + "@" + MoverParameters->TypeName, + asBaseDir + numer, + asBaseDir + to + "@" + MoverParameters->TypeName, + asBaseDir + to, + asBaseDir + "nowhere" }; + + for( auto const &destination : destinations ) { + + auto material = TextureTest( destination ); + if( false == material.empty() ) { + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( material ); + break; + } } - x = TextureTest(asBaseDir + numer ); - if (!x.empty()) - { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - return; - } - if (to.empty()) - to = "nowhere"; - x = TextureTest(asBaseDir + to + "@" + MoverParameters->TypeName); // w pierwszej kolejności z nazwą FIZ/MMD - if (!x.empty()) - { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - return; - } - x = TextureTest(asBaseDir + to); // na razie prymitywnie - if (!x.empty()) - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - else - { - x = TextureTest(asBaseDir + "nowhere"); // jak nie znalazł dedykowanej, to niech daje nowhere - if (!x.empty()) - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); - } - // Ra 2015-01: żeby zalogować błąd, trzeba by mieć pewność, że model używa tekstury nr 4 }; void TDynamicObject::OverheadTrack(float o) diff --git a/DynObj.h b/DynObj.h index 3509696a..88c9e0ea 100644 --- a/DynObj.h +++ b/DynObj.h @@ -143,7 +143,7 @@ class TAnim struct material_data { int textures_alpha{ 0x30300030 }; // maska przezroczystości tekstur. default: tekstury wymienne nie mają przezroczystości - texture_handle replacable_skins[ 5 ] = { NULL, NULL, NULL, NULL, NULL }; // McZapkie:zmienialne nadwozie + material_handle replacable_skins[ 5 ] = { null_handle, null_handle, null_handle, null_handle, null_handle }; // McZapkie:zmienialne nadwozie int multi_textures{ 0 }; //<0 tekstury wskazane wpisem, >0 tekstury z przecinkami, =0 jedna }; @@ -178,6 +178,7 @@ public: // parametry położenia pojazdu dostępne publicznie int NextConnectedNo; // numer sprzęgu podłączonego z tyłu int PrevConnectedNo; // numer sprzęgu podłączonego z przodu double fScanDist; // odległość skanowania torów na obecność innych pojazdów + double fTrackBlock; // odległość do przeszkody do dalszego ruchu (wykrywanie kolizji z innym pojazdem) TPowerSource ConnectedEnginePowerSource( TDynamicObject const *Caller ) const; @@ -186,7 +187,7 @@ public: // modele składowe pojazdu TModel3d *mdLoad; // model zmiennego ładunku TModel3d *mdKabina; // model kabiny dla użytkownika; McZapkie-030303: to z train.h TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly - float3 InteriorLight { 0.9f * 255.0f / 255.0f, 0.9f * 216.0f / 255.0f, 0.9f * 176.0f / 255.0f }; // tungsten light. TODO: allow definition of light type? + glm::vec3 InteriorLight { 0.9f * 255.f / 255.f, 0.9f * 216.f / 255.f, 0.9f * 176.f / 255.f }; // tungsten light. TODO: allow definition of light type? float InteriorLightLevel { 0.0f }; // current level of interior lighting struct section_light { TSubModel *compartment; @@ -354,22 +355,18 @@ public: // modele składowe pojazdu // 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 - int CouplCounter; std::string asModel; public: void ABuScanObjects(int ScanDir, double ScanDist); protected: - TDynamicObject * ABuFindObject(TTrack *Track, int ScanDir, BYTE &CouplFound, - double &dist); + TDynamicObject *ABuFindObject( int &Foundcoupler, double &Distance, TTrack *Track, int const Direction, int const Mycoupler ); void ABuCheckMyTrack(); public: int *iLights; // wskaźnik na bity zapalonych świateł (własne albo innego członu) bool DimHeadlights{ false }; // status of the headlight dimming toggle. NOTE: single toggle for all lights is a simplification. TODO: separate per-light switches - double fTrackBlock; // odległość do przeszkody do dalszego ruchu (wykrywanie kolizji z innym - // pojazdem) TDynamicObject * PrevAny(); TDynamicObject * Prev(); TDynamicObject * Next(); diff --git a/EU07.cpp b/EU07.cpp index 00852213..78d26c69 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -187,16 +187,16 @@ void key_callback( GLFWwindow *window, int key, int scancode, int action, int mo World.OnKeyDown( key ); +#ifdef CAN_I_HAS_LIBPNG switch( key ) { -#ifdef CAN_I_HAS_LIBPNG case GLFW_KEY_F11: { make_screenshot(); break; } -#endif default: { break; } } +#endif } } @@ -272,7 +272,7 @@ int main(int argc, char *argv[]) { auto const fileversionsize = ::GetFileVersionInfoSize( argv[ 0 ], NULL ); std::vectorfileversiondata; fileversiondata.resize( fileversionsize ); - if( ::GetFileVersionInfo( argv[ 0 ], NULL, fileversionsize, fileversiondata.data() ) ) { + if( ::GetFileVersionInfo( argv[ 0 ], 0, fileversionsize, fileversiondata.data() ) ) { struct lang_codepage { WORD language; @@ -423,7 +423,7 @@ int main(int argc, char *argv[]) Global::pWorld = &World; // Ra: wskaźnik potrzebny do usuwania pojazdów try { if( false == World.Init( window ) ) { - ErrorLog( "Failed to init TWorld" ); + ErrorLog( "Simulation setup failed" ); return -1; } } diff --git a/Event.cpp b/Event.cpp index a56c5cdf..1038d1be 100644 --- a/Event.cpp +++ b/Event.cpp @@ -78,7 +78,7 @@ void TEvent::Conditions(cParser *parser, std::string s) if (!asNodeName.empty()) { // podczepienie łańcucha, jeśli nie jest pusty // BUG: source of a memory leak -- the array never gets deleted. fix the destructor - Params[9].asText = new char[asNodeName.length() + 1]; // usuwane i zamieniane na + Params[9].asText = new char[asNodeName.size() + 1]; // usuwane i zamieniane na // wskaźnik strcpy(Params[9].asText, asNodeName.c_str()); } @@ -103,7 +103,7 @@ void TEvent::Conditions(cParser *parser, std::string s) str = token; if (str != "*") //"*" - nie brac command pod uwage { // zapamiętanie łańcucha do porównania - Params[10].asText = new char[255]; + Params[10].asText = new char[str.size() + 1]; strcpy(Params[10].asText, str.c_str()); iFlags |= conditional_memstring; } @@ -144,7 +144,6 @@ void TEvent::Load(cParser *parser, vector3 *org) int ti; std::string token; //string str; - char *ptr; bEnabled = true; // zmieniane na false dla eventów używanych do skanowania sygnałów @@ -222,14 +221,12 @@ void TEvent::Load(cParser *parser, vector3 *org) // if (Type==tp_UpdateValues) iFlags=0; //co modyfikować parser->getTokens(1, false); // case sensitive *parser >> token; - // str = AnsiString(token.c_str()); - Params[0].asText = new char[token.length() + 1]; // BUG: source of memory leak + Params[0].asText = new char[token.size() + 1]; // BUG: source of memory leak strcpy(Params[0].asText, token.c_str()); if (token != "*") // czy ma zostać bez zmian? iFlags |= update_memstring; parser->getTokens(); *parser >> token; - // str = AnsiString(token.c_str()); if (token != "*") // czy ma zostać bez zmian? { Params[1].asdouble = atof(token.c_str()); @@ -239,7 +236,6 @@ void TEvent::Load(cParser *parser, vector3 *org) Params[1].asdouble = 0; parser->getTokens(); *parser >> token; - // str = AnsiString(token.c_str()); if (token != "*") // czy ma zostać bez zmian? { Params[2].asdouble = atof(token.c_str()); @@ -262,7 +258,7 @@ void TEvent::Load(cParser *parser, vector3 *org) switch (++i) { // znaczenie kolejnych parametrów case 1: // nazwa drugiej komórki (źródłowej) - Params[9].asText = new char[token.length() + 1]; // usuwane i zamieniane na wskaźnik + Params[9].asText = new char[token.size() + 1]; // usuwane i zamieniane na wskaźnik strcpy(Params[9].asText, token.c_str()); break; case 2: // maska wartości @@ -353,7 +349,7 @@ void TEvent::Load(cParser *parser, vector3 *org) } else Params[6].asCommand = cm_Unknown; - Params[0].asText = new char[token.length() + 1]; + Params[0].asText = new char[token.size() + 1]; strcpy(Params[0].asText, token.c_str()); parser->getTokens(); *parser >> token; @@ -431,8 +427,7 @@ void TEvent::Load(cParser *parser, vector3 *org) *parser >> token; break; case tp_Exit: - while ((ptr = strchr(strdup(asNodeName.c_str()), '_')) != NULL) - *ptr = ' '; + asNodeName = ExchangeCharInString( asNodeName, '_', ' ' ); parser->getTokens(); *parser >> token; break; @@ -562,7 +557,7 @@ void TEvent::Load(cParser *parser, vector3 *org) { // eventy rozpoczynające się od "none_" są ignorowane if (token != "else") { - Params[i].asText = new char[255]; + Params[i].asText = new char[token.size() + 1]; strcpy(Params[i].asText, token.c_str()); if (ti) iFlags |= conditional_else << i; // oflagowanie dla eventów "else" diff --git a/Event.h b/Event.h index 7d020046..4eaebfe1 100644 --- a/Event.h +++ b/Event.h @@ -15,8 +15,7 @@ http://mozilla.org/MPL/2.0/. using namespace Math3D; -typedef enum -{ +enum TEventType { tp_Unknown, tp_Sound, tp_SoundPos, @@ -41,7 +40,7 @@ typedef enum tp_Voltage, tp_Message, tp_Friction -} TEventType; +}; const int update_memstring = 0x0000001; // zmodyfikować tekst (UpdateValues) const int update_memval1 = 0x0000002; // zmodyfikować pierwszą wartosć diff --git a/Gauge.cpp b/Gauge.cpp index c52de766..4def85c2 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -85,7 +85,7 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { } scale *= mul; - TSubModel *submodel = md1->GetFromName( submodelname.c_str() ); + TSubModel *submodel = md1->GetFromName( submodelname ); if( scale == 0.0 ) { ErrorLog( "Scale of 0.0 defined for sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\". Forcing scale of 1.0 to prevent division by 0" ); scale = 1.0; @@ -93,7 +93,7 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { if (submodel) // jeśli nie znaleziony md2 = nullptr; // informacja, że znaleziony else if (md2) // a jest podany drugi model (np. zewnętrzny) - submodel = md2->GetFromName(submodelname.c_str()); // to może tam będzie, co za różnica gdzie + submodel = md2->GetFromName(submodelname); // to może tam będzie, co za różnica gdzie if( submodel == nullptr ) { ErrorLog( "Failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\"" ); } @@ -116,7 +116,7 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { bool TGauge::Load_mapping( cParser &Input ) { - if( false == Input.getTokens( 2, true, ", " ) ) { + if( false == Input.getTokens( 2, true, ", \n\r\t" ) ) { return false; } diff --git a/Globals.cpp b/Globals.cpp index 80b4fa98..780b5697 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -51,7 +51,6 @@ bool Global::ControlPicking = false; // indicates controls pick mode is enabled bool Global::InputMouse = true; // whether control pick mode can be activated int Global::iTextMode = 0; // tryb pracy wyświetlacza tekstowego int Global::iScreenMode[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // numer ekranu wyświetlacza tekstowego -double Global::fSunDeclination = 0.0; // deklinacja Słońca double Global::fTimeAngleDeg = 0.0; // godzina w postaci kąta float Global::fClockAngleDeg[6]; // kąty obrotu cylindrów dla zegara cyfrowego std::string Global::szTexturesTGA = ".tga"; // lista tekstur od TGA @@ -63,7 +62,6 @@ int Global::iErorrCounter = 0; // licznik sprawdzań do śledzenia błędów Ope int Global::iTextures = 0; // licznik użytych tekstur TWorld *Global::pWorld = NULL; cParser *Global::pParser = NULL; -int Global::iSegmentsRendered = 90; // ilość segmentów do regulacji wydajności TCamera *Global::pCamera = NULL; // parametry kamery TDynamicObject *Global::pUserDynamic = NULL; // pojazd użytkownika, renderowany bez trzęsienia TTranscripts Global::tranTexts; // obiekt obsługujący stenogramy dźwięków na ekranie @@ -86,7 +84,8 @@ float Global::BaseDrawRange { 2500.f }; opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; bool Global::ScaleSpecularValues { true }; -bool Global::RenderShadows { false }; +bool Global::BasicRenderer { false }; +bool Global::RenderShadows { true }; Global::shadowtune_t Global::shadowtune = { 2048, 250.f, 250.f, 500.f }; bool Global::bRollFix = true; // czy wykonać przeliczanie przechyłki bool Global::bJoinEvents = false; // czy grupować eventy o tych samych nazwach @@ -127,10 +126,11 @@ int Global::iDefaultFiltering = 9; // domyślne rozmywanie tekstur TGA bez alfa int Global::iBallastFiltering = 9; // domyślne rozmywanie tekstur podsypki int Global::iRailProFiltering = 5; // domyślne rozmywanie tekstur szyn int Global::iDynamicFiltering = 5; // domyślne rozmywanie tekstur pojazdów -bool Global::bUseVBO = false; // czy jest VBO w karcie graficznej (czy użyć) +bool Global::bUseVBO = true; // czy jest VBO w karcie graficznej (czy użyć) std::string Global::LastGLError; GLint Global::iMaxTextureSize = 4096; // maksymalny rozmiar tekstury bool Global::bSmoothTraction = false; // wygładzanie drutów starym sposobem +float Global::SplineFidelity { 1.f }; // determines segment size during conversion of splines to geometry std::string Global::szDefaultExt = Global::szTexturesDDS; // domyślnie od DDS int Global::iMultisampling = 2; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px bool Global::DLFont{ false }; // switch indicating presence of basic font @@ -149,7 +149,6 @@ double Global::fFpsMax = 65.0; // górna granica FPS, przy której promień scen double Global::fFpsRadiusMax = 3000.0; // maksymalny promień renderowania int Global::iFpsRadiusMax = 225; // maksymalny promień renderowania double Global::fRadiusFactor = 1.1; // współczynnik jednorazowej zmiany promienia scenerii -bool Global::bOldSmudge = false; // Używanie starej smugi // parametry testowe (do testowania scenerii i obiektów) bool Global::bWireFrame = false; @@ -545,6 +544,13 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::ScaleSpecularValues; } + else if( token == "gfxrenderer" ) { + // shadow render toggle + std::string gfxrenderer; + Parser.getTokens(); + Parser >> gfxrenderer; + Global::BasicRenderer = ( gfxrenderer == "simple" ); + } else if( token == "shadows" ) { // shadow render toggle Parser.getTokens(); @@ -564,6 +570,13 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::bSmoothTraction; } + else if( token == "splinefidelity" ) { + // segment size during spline->geometry conversion + float splinefidelity; + Parser.getTokens(); + Parser >> splinefidelity; + Global::SplineFidelity = clamp( splinefidelity, 1.f, 4.f ); + } else if (token == "timespeed") { // przyspieszenie czasu, zmienna do testów @@ -623,12 +636,6 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::bHideConsole; } - else if (token == "oldsmudge") - { - - Parser.getTokens(); - Parser >> Global::bOldSmudge; - } else if (token == "rollfix") { // Ra: poprawianie przechyłki, aby wewnętrzna szyna była "pozioma" @@ -1304,12 +1311,5 @@ double Global::Min0RSpeed(double vel1, double vel2) { vel2 = std::numeric_limits::max(); } - return Min0R(vel1, vel2); -}; - -double Global::CutValueToRange(double min, double value, double max) -{ // przycinanie wartosci do podanych granic - value = Max0R(value, min); - value = Min0R(value, max); - return value; + return std::min(vel1, vel2); }; diff --git a/Globals.h b/Globals.h index 376a45a0..37fefd65 100644 --- a/Globals.h +++ b/Globals.h @@ -228,6 +228,7 @@ class Global static opengl_light DayLight; static int DynamicLightCount; static bool ScaleSpecularValues; + static bool BasicRenderer; static bool RenderShadows; static struct shadowtune_t { unsigned int map_size; @@ -273,7 +274,7 @@ class Global static double fMoveLight; // numer dnia w roku albo -1 static bool FakeLight; // toggle between fixed and dynamic daylight static bool bSmoothTraction; // wygładzanie drutów - static double fSunDeclination; // deklinacja Słońca + static float SplineFidelity; // determines segment size during conversion of splines to geometry static double fTimeSpeed; // przyspieszenie czasu, zmienna do testów static double fTimeAngleDeg; // godzina w postaci kąta static float fClockAngleDeg[6]; // kąty obrotu cylindrów dla zegara cyfrowego @@ -293,14 +294,12 @@ class Global static int iSlowMotionMask; // maska wyłączanych właściwości static int iModifyTGA; // czy korygować pliki TGA dla szybszego wczytywania static bool bHideConsole; // hunter-271211: ukrywanie konsoli - static bool bOldSmudge; // Używanie starej smugi static TWorld *pWorld; // wskaźnik na świat do usuwania pojazdów static TAnimModel *pTerrainCompact; // obiekt terenu do ewentualnego zapisania w pliku static std::string asTerrainModel; // nazwa obiektu terenu do zapisania w pliku static bool bRollFix; // czy wykonać przeliczanie przechyłki static cParser *pParser; - static int iSegmentsRendered; // ilość segmentów do regulacji wydajności static double fFpsAverage; // oczekiwana wartosć FPS static double fFpsDeviation; // odchylenie standardowe FPS static double fFpsMin; // dolna granica FPS, przy której promień scenerii będzie zmniejszany @@ -337,7 +336,6 @@ class Global static bool DoEvents(); static std::string Bezogonkow(std::string str, bool _ = false); static double Min0RSpeed(double vel1, double vel2); - static double CutValueToRange(double min, double value, double max); // maciek001: zmienne dla MWD static bool bMWDmasterEnable; // główne włączenie portu COM diff --git a/Ground.cpp b/Ground.cpp index 57b76e36..8c2a349d 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -67,7 +67,7 @@ TGroundNode::TGroundNode() nNext = nNext2 = NULL; iCount = 0; // wierzchołków w trójkącie // iNumPts=0; //punktów w linii - TextureID = 0; + m_material = 0; iFlags = 0; // tryb przezroczystości nie zbadany Pointer = NULL; // zerowanie wskaźnika kontekstowego bVisible = false; // czy widoczny @@ -232,7 +232,7 @@ void TSubRect::NodeAdd(TGroundNode *Node) // since ground rectangle can be empty, we're doing lazy initialization of the geometry bank, when something may actually use it // NOTE: this method is called for both subcell and cell, but subcells get first created and passed the handle from their parent // thus, this effectively only gets executed for the 'parent' ground cells. Not the most elegant, but for now it'll do - if( m_geometrybank == NULL ) { + if( m_geometrybank == null_handle ) { m_geometrybank = GfxRenderer.Create_Bank(); } @@ -314,7 +314,9 @@ void TSubRect::NodeAdd(TGroundNode *Node) // przygotowanie sektora do renderowania void TSubRect::Sort() { - assert( tTracks == nullptr ); + if( tTracks != nullptr ) { + SafeDelete( tTracks ); + } if( iTracks > 0 ) { tTracks = new TTrack *[ iTracks ]; // tworzenie tabeli torów do renderowania pojazdów int i = 0; @@ -354,13 +356,17 @@ bool TSubRect::RaTrackAnimAdd(TTrack *t) return false; // będzie animowane... } -void TSubRect::RaAnimate() -{ // wykonanie animacji - if( tTrackAnim == nullptr ) { +// wykonanie animacji +void TSubRect::RaAnimate( unsigned int const Framestamp ) { + + if( ( tTrackAnim == nullptr ) + || ( Framestamp == m_framestamp ) ) { // nie ma nic do animowania return; } tTrackAnim = tTrackAnim->RaAnimate(); // przeliczenie animacji kolejnego + + m_framestamp = Framestamp; }; TTraction * TSubRect::FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude) @@ -429,8 +435,6 @@ void TSubRect::RenderSounds() //--------------------------------------------------------------------------- //------------------ Kwadrat kilometrowy ------------------------------------ //--------------------------------------------------------------------------- -int TGroundRect::iFrameNumber = 0; // licznik wyświetlanych klatek - TGroundRect::~TGroundRect() { SafeDeleteArray(pSubRects); @@ -439,7 +443,7 @@ TGroundRect::~TGroundRect() void TGroundRect::Init() { // since ground rectangle can be empty, we're doing lazy initialization of the geometry bank, when something may actually use it - if( m_geometrybank == NULL ) { + if( m_geometrybank == null_handle ) { m_geometrybank = GfxRenderer.Create_Bank(); } @@ -486,12 +490,11 @@ TGroundRect::NodeAdd( TGroundNode *Node ) { matchingnode->pCenter, Node->pCenter, static_cast( Node->iNumVerts ) / ( Node->iNumVerts + matchingnode->iNumVerts ) ); matchingnode->iNumVerts += Node->iNumVerts; - matchingnode->Piece->vertices.resize( matchingnode->iNumVerts, TGroundVertex() ); matchingnode->Piece->vertices.insert( std::end( matchingnode->Piece->vertices ), std::begin( Node->Piece->vertices ), std::end( Node->Piece->vertices ) ); // clear content of the node we're copying. a minor memory saving at best, but still a saving - Node->Piece->vertices.swap( std::vector() ); + std::vector().swap( Node->Piece->vertices ); Node->iNumVerts = 0; // since we've put the data in existing node we can skip adding the new one... return; @@ -508,7 +511,7 @@ TGroundRect::mergeable( TGroundNode const &Left, TGroundNode const &Right ) { // since view ranges and transparency type for all nodes put through this method are guaranteed to be equal, // we can skip their tests and only do the material check. // TODO, TBD: material as dedicated type, and refactor this method into a simple equality test - return ( ( Left.TextureID == Right.TextureID ) + return ( ( Left.m_material == Right.m_material ) && ( Left.Ambient == Right.Ambient ) && ( Left.Diffuse == Right.Diffuse ) && ( Left.Specular == Right.Specular ) ); @@ -574,7 +577,6 @@ void TGround::Free() Current = Current->nNext; delete tmpn; } - iNumNodes = 0; // RootNode=NULL; nRootDynamic = NULL; } @@ -690,7 +692,6 @@ TGround::convert_terrain( TSubModel const *Submodel ) { groundnode->nNext = nRootOfType[ groundnode->iType ]; // ustawienie nowego na początku listy nRootOfType[ groundnode->iType ] = groundnode; - ++iNumNodes; } else { delete groundnode; @@ -776,7 +777,7 @@ void TGround::RaTriangleDivider(TGroundNode *node) TGroundNode *ntri; // wskaźnik na nowy trójkąt ntri = new TGroundNode(GL_TRIANGLES); // a ten jest nowy // kopiowanie parametrów, przydałby się konstruktor kopiujący - ntri->TextureID = node->TextureID; + ntri->m_material = node->m_material; ntri->iFlags = node->iFlags; ntri->Ambient = node->Ambient; ntri->Diffuse = node->Diffuse; @@ -787,7 +788,6 @@ void TGround::RaTriangleDivider(TGroundNode *node) ntri->bVisible = node->bVisible; // a są jakieś niewidoczne? ntri->nNext = nRootOfType[GL_TRIANGLES]; nRootOfType[GL_TRIANGLES] = ntri; // dopisanie z przodu do listy - ++iNumNodes; ntri->iNumVerts = 3; ntri->Piece->vertices.resize( 3 ); switch (divide & 3) @@ -1376,23 +1376,36 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) *parser >> token; } str = token; - tmp->TextureID = GfxRenderer.Fetch_Texture( str ); + tmp->m_material = GfxRenderer.Fetch_Material( str ); + auto const texturehandle = ( + tmp->m_material != null_handle ? + GfxRenderer.Material( tmp->m_material ).texture1 : + null_handle ); + auto const &texture = ( + texturehandle ? + GfxRenderer.Texture( texturehandle ) : + opengl_texture() ); // dirty workaround for lack of better api bool const clamps = ( - tmp->TextureID ? - GfxRenderer.Texture( tmp->TextureID ).traits.find( 's' ) != std::string::npos : + texturehandle ? + texture.traits.find( 's' ) != std::string::npos : false ); bool const clampt = ( - tmp->TextureID ? - GfxRenderer.Texture( tmp->TextureID ).traits.find( 't' ) != std::string::npos : + texturehandle ? + texture.traits.find( 't' ) != std::string::npos : false ); tmp->iFlags |= 200; // z usuwaniem // remainder of legacy 'problend' system -- geometry assigned a texture with '@' in its name is treated as translucent, opaque otherwise - tmp->iFlags |= ( - ( ( str.find( '@' ) != std::string::npos ) - && ( true == GfxRenderer.Texture( tmp->TextureID ).has_alpha ) ) ? - 0x20 : - 0x10 ); + if( texturehandle != null_handle ) { + tmp->iFlags |= ( + ( ( texture.name.find( '@' ) != std::string::npos ) + && ( true == texture.has_alpha ) ) ? + 0x20 : + 0x10 ); + } + else { + tmp->iFlags |= 0x10; + } TGroundVertex vertex, vertex1, vertex2; std::size_t vertexcount{ 0 }; @@ -1586,7 +1599,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) importedvertices.emplace_back( vertex0 ); } if( importedvertices.size() % 2 != 0 ) { - ErrorLog( "Lines node specified odd number of vertices, encountered in file \"" + parser->Name() + "\"" ); + ErrorLog( "Lines node specified odd number of vertices, encountered in file \"" + parser->Name() + "\" (line " + std::to_string( parser->Line() - 1 ) + ")" ); importedvertices.pop_back(); } tmp->iType = GL_LINES; @@ -1641,6 +1654,8 @@ TSubRect * TGround::GetSubRect(int iCol, int iRow) TEvent * TGround::FindEvent(const std::string &asEventName) { + if( asEventName.empty() ) { return nullptr; } + auto const lookup = m_eventmap.find( asEventName ); return ( lookup != m_eventmap.end() ? @@ -1756,7 +1771,6 @@ bool TGround::Init(std::string File) // pTrain=NULL; pOrigin = aRotate = vector3(0, 0, 0); // zerowanie przesunięcia i obrotu std::string str; - // TFileStream *fs; // int size; std::string subpath = Global::asCurrentSceneryPath; // "scenery/"; cParser parser(File, cParser::buffer_FILE, subpath, Global::bLoadTraction); @@ -1765,7 +1779,6 @@ bool TGround::Init(std::string File) std::stack OriginStack; // stos zagnieżdżenia origin TGroundNode *LastNode = nullptr; // do użycia w trainset - iNumNodes = 0; token = ""; parser.getTokens(); parser >> token; @@ -1822,7 +1835,6 @@ bool TGround::Init(std::string File) LastNode->nNext = nRootOfType[ LastNode->iType ]; // ustawienie nowego na początku listy nRootOfType[ LastNode->iType ] = LastNode; - ++iNumNodes; } else { // jeśli jest pojazdem if( ( LastNode->DynamicObject->Mechanik != nullptr ) @@ -1858,7 +1870,7 @@ bool TGround::Init(std::string File) } else { - ErrorLog("Scene parsing error in file \"" + parser.Name() + "\", unexpected token \"" + token + "\""); + ErrorLog("Scene parsing error in file \"" + parser.Name() + "\" (line " + std::to_string( parser.Line() ) + "), unexpected token \"" + token + "\""); // break; } } @@ -2198,7 +2210,7 @@ bool TGround::Init(std::string File) bool TGround::InitEvents() { //łączenie eventów z pozostałymi obiektami TGroundNode *tmp, *trk; - char buff[ 255 ]; std::string cellastext; + std::string cellastext; int i; for (TEvent *Current = RootEvent; Current; Current = Current->evNext2) { @@ -2206,19 +2218,16 @@ bool TGround::InitEvents() { case tp_AddValues: // sumowanie wartości case tp_UpdateValues: // zmiana wartości - tmp = FindGroundNode(Current->asNodeName, - TP_MEMCELL); // nazwa komórki powiązanej z eventem + tmp = FindGroundNode(Current->asNodeName, TP_MEMCELL); // nazwa komórki powiązanej z eventem if (tmp) { // 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) - trk = FindGroundNode(Current->asNodeName, - TP_TRACK); // nazwa toru ta sama, co nazwa komórki + trk = FindGroundNode(Current->asNodeName, TP_TRACK); // nazwa toru ta sama, co nazwa komórki if (trk) Current->Params[9].asTrack = trk->pTrack; if (!Current->Params[9].asTrack) - ErrorLog("Bad event: track \"" + Current->asNodeName + - "\" does not exists in \"" + Current->asName + "\""); + ErrorLog("Bad event: track \"" + Current->asNodeName + "\" referenced in event \"" + Current->asName + "\" doesn't exist"); } Current->Params[4].nGroundNode = tmp; Current->Params[5].asMemCell = tmp->MemCell; // komórka do aktualizacji @@ -2231,8 +2240,7 @@ bool TGround::InitEvents() if (trk) Current->Params[6].asTrack = trk->pTrack; else - ErrorLog("Bad memcell: track \"" + tmp->MemCell->asTrackName + - "\" not exists in memcell \"" + tmp->asName + "\""); + ErrorLog("Bad memcell: track \"" + tmp->MemCell->asTrackName + "\" referenced in memcell \"" + tmp->asName + "\" doesn't exist"); } else Current->Params[6].asTrack = NULL; @@ -2240,8 +2248,7 @@ bool TGround::InitEvents() else { // nie ma komórki, to nie będzie działał poprawnie Current->Type = tp_Ignored; // deaktywacja - ErrorLog("Bad event: \"" + Current->asName + "\" cannot find memcell \"" + - Current->asNodeName + "\""); + ErrorLog("Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); } break; case tp_LogValues: // skojarzenie z memcell @@ -2265,8 +2272,7 @@ bool TGround::InitEvents() else { // nie ma komórki, to nie będzie działał poprawnie Current->Type = tp_Ignored; // deaktywacja - ErrorLog("Bad event: \"" + Current->asName + "\" cannot find memcell \"" + - Current->asNodeName + "\""); + ErrorLog("Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); } break; case tp_CopyValues: // skopiowanie komórki do innej @@ -2282,15 +2288,13 @@ bool TGround::InitEvents() if (trk) Current->Params[6].asTrack = trk->pTrack; else - ErrorLog("Bad memcell: track \"" + tmp->MemCell->asTrackName + - "\" not exists in memcell \"" + tmp->asName + "\""); + ErrorLog("Bad memcell: track \"" + tmp->MemCell->asTrackName + "\" referenced in memcell \"" + tmp->asName + "\" doesn't exists"); } else Current->Params[6].asTrack = NULL; } else - ErrorLog("Bad copyvalues: event \"" + Current->asName + - "\" cannot find memcell \"" + Current->asNodeName + "\""); + ErrorLog("Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); cellastext = Current->Params[ 9 ].asText; SafeDeleteArray(Current->Params[9].asText); // usunięcie nazwy komórki tmp = FindGroundNode( cellastext, TP_MEMCELL); // komórka źódłowa @@ -2299,22 +2303,19 @@ bool TGround::InitEvents() Current->Params[9].asMemCell = tmp->MemCell; // komórka źródłowa } else - ErrorLog("Bad copyvalues: event \"" + Current->asName + - "\" cannot find memcell \"" + cellastext + "\""); + ErrorLog("Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + cellastext + "\""); break; case tp_Animation: // animacja modelu tmp = FindGroundNode(Current->asNodeName, TP_MODEL); // egzemplarza modelu do animowania if (tmp) { - strcpy( - buff, - Current->Params[9].asText); // skopiowanie nazwy submodelu do bufora roboczego + cellastext = Current->Params[9].asText; // skopiowanie nazwy submodelu do bufora roboczego SafeDeleteArray(Current->Params[9].asText); // usunięcie nazwy submodelu if (Current->Params[0].asInt == 4) Current->Params[9].asModel = tmp->Model; // model dla całomodelowych animacji else { // standardowo przypisanie submodelu - Current->Params[9].asAnimContainer = tmp->Model->GetContainer(buff); // submodel + Current->Params[9].asAnimContainer = tmp->Model->GetContainer(cellastext); // submodel if (Current->Params[9].asAnimContainer) { Current->Params[9].asAnimContainer->WillBeAnimated(); // oflagowanie @@ -2322,13 +2323,12 @@ bool TGround::InitEvents() if (!Current->Params[9] .asAnimContainer->Event()) // nie szukać, gdy znaleziony Current->Params[9].asAnimContainer->EventAssign( - FindEvent(Current->asNodeName + "." + buff + ":done")); + FindEvent(Current->asNodeName + "." + cellastext + ":done")); } } } else - ErrorLog("Bad animation: event \"" + Current->asName + "\" cannot find model \"" + - Current->asNodeName + "\""); + ErrorLog("Bad event: animation event \"" + Current->asName + "\" cannot find model \"" + Current->asNodeName + "\""); Current->asNodeName = ""; break; case tp_Lights: // zmiana świeteł modelu @@ -2336,8 +2336,7 @@ bool TGround::InitEvents() if (tmp) Current->Params[9].asModel = tmp->Model; else - ErrorLog("Bad lights: event \"" + Current->asName + "\" cannot find model \"" + - Current->asNodeName + "\""); + ErrorLog("Bad event: lights event \"" + Current->asName + "\" cannot find model \"" + Current->asNodeName + "\""); Current->asNodeName = ""; break; case tp_Visible: // ukrycie albo przywrócenie obiektu @@ -2349,8 +2348,7 @@ bool TGround::InitEvents() if (tmp) Current->Params[9].nGroundNode = tmp; else - ErrorLog("Bad visibility: event \"" + Current->asName + "\" cannot find model \"" + - Current->asNodeName + "\""); + ErrorLog("Bad event: visibility event \"" + Current->asName + "\" cannot find model \"" + Current->asNodeName + "\""); Current->asNodeName = ""; break; case tp_Switch: // przełożenie zwrotnicy albo zmiana stanu obrotnicy @@ -2368,8 +2366,7 @@ bool TGround::InitEvents() Current->Params[2].asdouble); // przesłanie parametrów } else - ErrorLog("Bad switch: event \"" + Current->asName + "\" cannot find track \"" + - Current->asNodeName + "\""); + ErrorLog("Bad event: switch event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\""); Current->asNodeName = ""; break; case tp_Sound: // odtworzenie dźwięku @@ -2377,8 +2374,7 @@ bool TGround::InitEvents() if (tmp) Current->Params[9].tsTextSound = tmp->tsStaticSound; else - ErrorLog("Bad sound: event \"" + Current->asName + - "\" cannot find static sound \"" + Current->asNodeName + "\""); + 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 @@ -2392,8 +2388,7 @@ bool TGround::InitEvents() Current->Params[9].asTrack = tmp->pTrack; } else - ErrorLog("Bad velocity: event \"" + Current->asName + - "\" cannot find track \"" + Current->asNodeName + "\""); + ErrorLog("Bad event: track velocity event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\""); } Current->asNodeName = ""; break; @@ -2406,54 +2401,53 @@ bool TGround::InitEvents() if (tmp) Current->Params[9].asDynamic = tmp->DynamicObject; else - Error("Event \"" + Current->asName + "\" cannot find dynamic \"" + - Current->asNodeName + "\""); + Error("Bad event: vehicle velocity event \"" + Current->asName + "\" cannot find vehicle \"" + Current->asNodeName + "\""); } Current->asNodeName = ""; break; case tp_Multiple: if (Current->Params[9].asText != NULL) { // przepisanie nazwy do bufora - strcpy(buff, Current->Params[9].asText); + cellastext = Current->Params[ 9 ].asText; SafeDeleteArray(Current->Params[9].asText); Current->Params[9].asPointer = NULL; // zerowanie wskaźnika, aby wykryć brak obeiktu } else - buff[0] = '\0'; + cellastext = ""; if (Current->iFlags & (conditional_trackoccupied | conditional_trackfree)) { // jeśli chodzi o zajetosc toru - tmp = FindGroundNode(buff, TP_TRACK); + tmp = FindGroundNode(cellastext, TP_TRACK); if (tmp) Current->Params[9].asTrack = tmp->pTrack; if (!Current->Params[9].asTrack) { - ErrorLog("Bad event: Track \"" + std::string(buff) + "\" does not exist in \"" + Current->asName + "\""); + ErrorLog("Bad event: Track \"" + cellastext + "\" does not exist in \"" + Current->asName + "\""); Current->iFlags &= ~(conditional_trackoccupied | conditional_trackfree); // zerowanie flag } } else if (Current->iFlags & (conditional_memstring | conditional_memval1 | conditional_memval2)) { // jeśli chodzi o komorke pamieciową - tmp = FindGroundNode(buff, TP_MEMCELL); + tmp = FindGroundNode(cellastext, TP_MEMCELL); if (tmp) Current->Params[9].asMemCell = tmp->MemCell; if (!Current->Params[9].asMemCell) { - ErrorLog("Bad event: MemCell \"" + std::string(buff) + "\" does not exist in \"" + Current->asName + "\""); + ErrorLog("Bad event: MemCell \"" + cellastext + "\" does not exist in \"" + Current->asName + "\""); Current->iFlags &= ~(conditional_memstring | conditional_memval1 | conditional_memval2); } } - for (i = 0; i < 8; i++) + for (i = 0; i < 8; ++i) { if (Current->Params[i].asText != NULL) { - strcpy(buff, Current->Params[i].asText); + cellastext = Current->Params[ i ].asText; SafeDeleteArray(Current->Params[i].asText); - Current->Params[i].asEvent = FindEvent(buff); + Current->Params[i].asEvent = FindEvent(cellastext); if( !Current->Params[ i ].asEvent ) { // Ra: tylko w logu informacja o braku if( ( Current->Params[ i ].asText == NULL ) || ( std::string( Current->Params[ i ].asText ).substr( 0, 5 ) != "none_" ) ) { - WriteLog( "Event \"" + std::string( buff ) + "\" does not exist" ); - ErrorLog( "Missed event: " + std::string( buff ) + " in multiple " + Current->asName ); + WriteLog( "Event \"" + cellastext + "\" does not exist" ); + ErrorLog( "Missed event: " + cellastext + " in multiple " + Current->asName ); } } } @@ -2462,13 +2456,11 @@ bool TGround::InitEvents() case tp_Voltage: // zmiana napięcia w zasilaczu (TractionPowerSource) if (!Current->asNodeName.empty()) { - tmp = FindGroundNode(Current->asNodeName, - TP_TRACTIONPOWERSOURCE); // podłączenie zasilacza + tmp = FindGroundNode(Current->asNodeName, TP_TRACTIONPOWERSOURCE); // podłączenie zasilacza if (tmp) Current->Params[9].psPower = tmp->psTractionPowerSource; else - ErrorLog("Bad voltage: event \"" + Current->asName + - "\" cannot find power source \"" + Current->asNodeName + "\""); + ErrorLog("Bad event: voltage event \"" + Current->asName + "\" cannot find power source \"" + Current->asNodeName + "\""); } Current->asNodeName = ""; break; @@ -2491,43 +2483,32 @@ void TGround::InitTracks() TTrack *tmp; // znaleziony tor TTrack *Track; int iConnection; - std::string name; - // tracks=tracksfar=0; - for (Current = nRootOfType[TP_TRACK]; Current; Current = Current->nNext) - { - Track = Current->pTrack; - if (Global::iHiddenEvents & 1) - if (!Current->asName.empty()) - { // jeśli podana jest nazwa torów, można szukać eventów skojarzonych przez nazwę - if (Track->asEvent0Name.empty()) - if (FindEvent(Current->asName + ":event0")) - Track->asEvent0Name = Current->asName + ":event0"; - if (Track->asEvent1Name.empty()) - if (FindEvent(Current->asName + ":event1")) - Track->asEvent1Name = Current->asName + ":event1"; - if (Track->asEvent2Name.empty()) - if (FindEvent(Current->asName + ":event2")) - Track->asEvent2Name = Current->asName + ":event2"; - if (Track->asEventall0Name.empty()) - if (FindEvent(Current->asName + ":eventall0")) - Track->asEventall0Name = Current->asName + ":eventall0"; - if (Track->asEventall1Name.empty()) - if (FindEvent(Current->asName + ":eventall1")) - Track->asEventall1Name = Current->asName + ":eventall1"; - if (Track->asEventall2Name.empty()) - if (FindEvent(Current->asName + ":eventall2")) - Track->asEventall2Name = Current->asName + ":eventall2"; - } + for (Current = nRootOfType[TP_TRACK]; Current; Current = Current->nNext) { + + Track = Current->pTrack; + // assign track events Track->AssignEvents( - Track->asEvent0Name.empty() ? NULL : FindEvent(Track->asEvent0Name), - Track->asEvent1Name.empty() ? NULL : FindEventScan(Track->asEvent1Name), - Track->asEvent2Name.empty() ? NULL : FindEventScan(Track->asEvent2Name)); + FindEvent( Track->asEvent0Name ), + FindEvent( Track->asEvent1Name ), + FindEvent( Track->asEvent2Name ) ); Track->AssignallEvents( - Track->asEventall0Name.empty() ? NULL : FindEvent(Track->asEventall0Name), - Track->asEventall1Name.empty() ? NULL : FindEvent(Track->asEventall1Name), - Track->asEventall2Name.empty() ? NULL : - FindEvent(Track->asEventall2Name)); // MC-280503 + FindEvent( Track->asEventall0Name ), + FindEvent( Track->asEventall1Name ), + FindEvent( Track->asEventall2Name ) ); + if( ( Global::iHiddenEvents & 1 ) + && ( false == Current->asName.empty() ) ) { + // jeśli podana jest nazwa torów, można szukać eventów skojarzonych przez nazwę + Track->AssignEvents( + FindEvent( Current->asName + ":event0" ), + FindEvent( Current->asName + ":event1" ), + FindEvent( Current->asName + ":event2" ) ); + Track->AssignallEvents( + FindEvent( Current->asName + ":eventall0" ), + FindEvent( Current->asName + ":eventall1" ), + FindEvent( Current->asName + ":eventall2" ) ); + } + switch (Track->eType) { case tt_Table: // obrotnicę też łączymy na starcie z innymi torami @@ -2625,7 +2606,7 @@ void TGround::InitTracks() FindEvent(Current->asName + ":forced-")); break; } - name = Track->IsolatedName(); // pobranie nazwy odcinka izolowanego + std::string const name = Track->IsolatedName(); // pobranie nazwy odcinka izolowanego if (!name.empty()) // jeśli została zwrócona nazwa Track->IsolatedEventsAssign(FindEvent(name + ":busy"), FindEvent(name + ":free")); if (Current->asName.substr(0, 1) == @@ -2649,7 +2630,6 @@ void TGround::InitTracks() Current->nNext = nRootOfType[TP_MEMCELL]; // to nie powinno tutaj być, bo robi się śmietnik nRootOfType[TP_MEMCELL] = Current; - iNumNodes++; p->pMemCell = Current->MemCell; // wskaźnik komóki przekazany do odcinka izolowanego } p = p->Next(); @@ -2688,7 +2668,6 @@ void TGround::InitTraction() nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany dołączamy na końcu // nowego nRootOfType[nTemp->iType] = nTemp; // ustawienie nowego na początku listy - iNumNodes++; } } for (nCurrent = nRootOfType[TP_TRACTION]; nCurrent; nCurrent = nCurrent->nNext) @@ -3134,9 +3113,8 @@ bool TGround::EventConditon(TEvent *e) return (e->Params[9].asTrack->IsEmpty()); else if (e->iFlags & conditional_propability) { - double rprobability = 1.0 * rand() / RAND_MAX; - WriteLog("Random integer: " + std::to_string(rprobability) + "/" + - std::to_string(e->Params[10].asdouble)); + double rprobability = Random(); + WriteLog("Random integer: " + std::to_string(rprobability) + " / " + std::to_string(e->Params[10].asdouble)); return (e->Params[10].asdouble > rprobability); } else if (e->iFlags & conditional_memcompare) diff --git a/Ground.h b/Ground.h index d8b02ad9..a35f69f5 100644 --- a/Ground.h +++ b/Ground.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "VBO.h" #include "Classes.h" #include "ResourceManager.h" -#include "Texture.h" +#include "material.h" #include "dumb3d.h" #include "Float3d.h" #include "Names.h" @@ -140,7 +140,7 @@ public: int iCount; // dla terenu }; int iFlags; // tryb przezroczystości: 0x10-nieprz.,0x20-przezroczysty,0x30-mieszany - texture_handle TextureID; // główna (jedna) tekstura obiektu + material_handle m_material; // główna (jedna) tekstura obiektu glm::vec3 Ambient{ 1.0f, 1.0f, 1.0f }, Diffuse{ 1.0f, 1.0f, 1.0f }, @@ -151,13 +151,8 @@ public: TGroundNode(); TGroundNode(TGroundNodeType t); ~TGroundNode(); -/* - void Init(int n); -*/ + void InitNormals(); -/* - void Release(); -*/ void RenderHidden(); // obsługa dźwięków i wyzwalaczy zdarzeń }; @@ -171,6 +166,7 @@ class TSubRect : /*public Resource,*/ public CMesh { // sektor składowy kwadratu kilometrowego public: bounding_area m_area; + unsigned int m_framestamp { 0 }; // id of last rendered gfx frame int iTracks = 0; // ilość torów w (tTracks) TTrack **tTracks = nullptr; // tory do renderowania pojazdów protected: @@ -192,22 +188,18 @@ class TSubRect : /*public Resource,*/ public CMesh void LoadNodes(); // utworzenie VBO sektora public: virtual ~TSubRect(); -/* - virtual void Release(); // zwalnianie VBO sektora -*/ virtual void NodeAdd(TGroundNode *Node); // dodanie obiektu do sektora na etapie rozdzielania na sektory void Sort(); // optymalizacja obiektów w sektorze (sortowanie wg tekstur) TTrack * FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude); TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude); bool RaTrackAnimAdd(TTrack *t); // zgłoszenie toru do animacji - void RaAnimate(); // przeliczenie animacji torów + void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji torów void RenderSounds(); // dźwięki pojazdów z niewidocznych sektorów }; // Ra: trzeba sprawdzić wydajność siatki const int iNumSubRects = 5; // na ile dzielimy kilometr const int iNumRects = 500; -// const double fHalfNumRects=iNumRects/2.0; //połowa do wyznaczenia środka const int iTotalNumSubRects = iNumRects * iNumSubRects; const double fHalfTotalNumSubRects = iTotalNumSubRects / 2.0; const double fSubRectSize = 1000.0 / iNumSubRects; @@ -221,7 +213,6 @@ class TGroundRect : public TSubRect private: TSubRect *pSubRects { nullptr }; - int iLastDisplay; // numer klatki w której był ostatnio wyświetlany void Init(); @@ -251,7 +242,6 @@ public: // optymalizacja obiektów w sektorach pSubRects[ i ].Sort(); } } }; - static int iFrameNumber; // numer kolejny wyświetlanej klatki TGroundNode *nTerrain { nullptr }; // model terenu z E3D - użyć nRootMesh? }; @@ -259,27 +249,22 @@ class TGround { friend class opengl_renderer; - vector3 CameraDirection; // zmienna robocza przy renderowaniu - int const *iRange = nullptr; // tabela widoczności + TGroundRect Rects[ iNumRects ][ iNumRects ]; // mapa kwadratów kilometrowych + TSubRect srGlobal; // zawiera obiekty globalne (na razie wyzwalacze czasowe) TGroundNode *nRootDynamic = nullptr; // lista pojazdów - TGroundRect Rects[iNumRects][iNumRects]; // mapa kwadratów kilometrowych + TGroundNode *nRootOfType[ TP_LAST ]; // tablica grupująca obiekty, przyspiesza szukanie TEvent *RootEvent = nullptr; // lista zdarzeń TEvent *QueryRootEvent = nullptr, *tmpEvent = nullptr; -/* - TSubRect *pRendered[1500]; // lista renderowanych sektorów -*/ - int iNumNodes = 0; - vector3 pOrigin; - vector3 aRotate; - bool bInitDone = false; - TGroundNode *nRootOfType[TP_LAST]; // tablica grupująca obiekty, przyspiesza szukanie - TSubRect srGlobal; // zawiera obiekty globalne (na razie wyzwalacze czasowe) typedef std::unordered_map event_map; event_map m_eventmap; TNames m_trackmap; light_array m_lights; // collection of dynamic light sources present in the scene + vector3 pOrigin; + vector3 aRotate; + bool bInitDone = false; + private: // metody prywatne bool EventConditon(TEvent *e); @@ -337,9 +322,6 @@ class TGround public: void WyslijEvent(const std::string &e, const std::string &d); -/* - int iRendered; // ilość renderowanych sektorów, pobierana przy pokazywniu FPS -*/ void WyslijString(const std::string &t, int n); void WyslijWolny(const std::string &t); void WyslijNamiary(TGroundNode *t); @@ -359,4 +341,5 @@ class TGround void IsolatedBusy(const std::string t); void Silence(vector3 gdzie); }; + //--------------------------------------------------------------------------- diff --git a/Logs.cpp b/Logs.cpp index 3770f7d9..13ae31bd 100644 --- a/Logs.cpp +++ b/Logs.cpp @@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/. std::ofstream output; // standardowy "log.txt", można go wyłączyć std::ofstream errors; // lista błędów "errors.txt", zawsze działa std::ofstream comms; // lista komunikatow "comms.txt", można go wyłączyć +char logbuffer[ 256 ]; char endstring[10] = "\n"; @@ -22,8 +23,9 @@ std::string filename_date() { ::SYSTEMTIME st; ::GetLocalTime( &st ); - char buffer[ 256 ]; - sprintf( buffer, + std::snprintf( + logbuffer, + sizeof(logbuffer), "%d%02d%02d_%02d%02d", st.wYear, st.wMonth, @@ -31,7 +33,7 @@ std::string filename_date() { st.wHour, st.wMinute ); - return std::string( buffer ); + return std::string( logbuffer ); } std::string filename_scenery() { @@ -45,13 +47,12 @@ std::string filename_scenery() { } } -void WriteConsoleOnly(const char *str, double value) -{ - char buf[255]; - sprintf(buf, "%s %f \n", str, value); +void WriteConsoleOnly(const char *str, double value) { + + std::snprintf(logbuffer , sizeof(logbuffer), "%s %f \n", str, value); // stdout= GetStdHandle(STD_OUTPUT_HANDLE); DWORD wr = 0; - WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), buf, (DWORD)strlen(buf), &wr, NULL); + WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), logbuffer, (DWORD)strlen(logbuffer), &wr, NULL); // WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),endstring,strlen(endstring),&wr,NULL); } @@ -66,18 +67,16 @@ void WriteConsoleOnly(const char *str, bool newline) WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), endstring, (DWORD)strlen(endstring), &wr, NULL); } -void WriteLog(const char *str, double value) -{ - if (Global::iWriteLogEnabled) - { - if (str) - { - char buf[255]; - sprintf(buf, "%s %f", str, value); - WriteLog(buf); +void WriteLog(const char *str, double value) { + + if (Global::iWriteLogEnabled) { + if (str) { + std::snprintf(logbuffer, sizeof(logbuffer), "%s %f", str, value); + WriteLog(logbuffer); } } }; + void WriteLog(const char *str, bool newline) { if (str) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 2fe15ace..bcb743c9 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -867,7 +867,8 @@ public: /* int BrakeStatus = b_off; //0 - odham, 1 - ham., 2 - uszk., 4 - odluzniacz, 8 - antyposlizg, 16 - uzyte EP, 32 - pozycja R, 64 - powrot z R */ - bool EmergencyBrakeFlag = false; /*hamowanie nagle*/ + bool AlarmChainFlag = false; // manual emergency brake + bool RadioStopFlag = false; /*hamowanie nagle*/ int BrakeDelayFlag = 0; /*nastawa opoznienia ham. osob/towar/posp/exp 0/1/2/4*/ int BrakeDelays = 0; /*nastawy mozliwe do uzyskania*/ int BrakeOpModeFlag = 0; /*nastawa trybu pracy PS/PN/EP/MED 1/2/4/8*/ @@ -1024,7 +1025,7 @@ public: bool IncBrakeLevel(); // wersja na użytek AI bool DecBrakeLevel(); bool ChangeCab(int direction); - bool CurrentSwitch(int direction); + bool CurrentSwitch(bool const State); void UpdateBatteryVoltage(double dt); double ComputeMovement(double dt, double dt1, const TTrackShape &Shape, TTrackParam &Track, TTractionParam &ElectricTraction, const TLocation &NewLoc, TRotation &NewRot); //oblicza przesuniecie pojazdu double FastComputeMovement(double dt, const TTrackShape &Shape, TTrackParam &Track, const TLocation &NewLoc, TRotation &NewRot); //oblicza przesuniecie pojazdu - wersja zoptymalizowana @@ -1082,7 +1083,8 @@ public: bool IncManualBrakeLevel(int CtrlSpeed); bool DecManualBrakeLevel(int CtrlSpeed); bool DynamicBrakeSwitch(bool Switch); - bool EmergencyBrakeSwitch(bool Switch); + bool RadiostopSwitch(bool Switch); + bool AlarmChainSwitch( bool const State ); bool AntiSlippingBrake(void); bool BrakeReleaser(int state); bool SwitchEPBrake(int state); @@ -1109,6 +1111,8 @@ public: double Adhesive(double staticfriction); double TractionForce(double dt); double FrictionForce(double R, int TDamage); + double BrakeForceR(double ratio, double velocity); + double BrakeForceP(double press, double velocity); double BrakeForce(const TTrackParam &Track); double CouplerForce(int CouplerN, double dt); void CollisionDetect(int CouplerN, double dt); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b24b268e..53453b95 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -692,13 +692,13 @@ bool TMoverParameters::ChangeCab(int direction) return false; }; -bool TMoverParameters::CurrentSwitch(int direction) -{ // rozruch wysoki (true) albo niski (false) +// rozruch wysoki (true) albo niski (false) +bool +TMoverParameters::CurrentSwitch(bool const State) { // Ra: przeniosłem z Train.cpp, nie wiem czy ma to sens - if (MaxCurrentSwitch(direction != 0)) - { + if (MaxCurrentSwitch(State)) { if (TrainType != dt_EZT) - return (MinCurrentSwitch(direction != 0)); + return (MinCurrentSwitch(State)); } // TBD, TODO: split off shunt mode toggle into a separate command? It doesn't make much sense to have these two together like that // dla 2Ls150 @@ -706,14 +706,14 @@ bool TMoverParameters::CurrentSwitch(int direction) && ( true == ShuntModeAllow ) && ( ActiveDir == 0 ) ) { // przed ustawieniem kierunku - ShuntMode = ( direction != 0 ); + ShuntMode = State; return true; } // for SM42/SP42 if( ( EngineType == DieselElectric ) && ( true == ShuntModeAllow ) && ( MainCtrlPos == 0 ) ) { - ShuntMode = ( direction != 0 ); + ShuntMode = State; return true; } @@ -1082,7 +1082,7 @@ void TMoverParameters::CollisionDetect(int CouplerN, double dt) EventFlag = true; if ((coupler.CouplingFlag & ctrain_pneumatic) == ctrain_pneumatic) - EmergencyBrakeFlag = true; // hamowanie nagle - zerwanie przewodow hamulcowych + AlarmChainFlag = true; // hamowanie nagle - zerwanie przewodow hamulcowych coupler.CouplingFlag = 0; switch (CouplerN) // wyzerowanie flag podlaczenia ale ciagle sa wirtualnie polaczone @@ -2188,7 +2188,7 @@ void TMoverParameters::SecuritySystemCheck(double dt) // obsady // poza tym jest zdefiniowany we wszystkich 3 członach EN57 if ((!Radio)) - EmergencyBrakeSwitch(false); + RadiostopSwitch(false); if ((SecuritySystem.SystemType > 0) && (SecuritySystem.Status > 0) && (Battery)) // Ra: EZT ma teraz czuwak w rozrządczym @@ -2259,7 +2259,7 @@ void TMoverParameters::SecuritySystemCheck(double dt) } else if (!Battery) { // wyłączenie baterii deaktywuje sprzęt - EmergencyBrakeSwitch(false); + RadiostopSwitch(false); // SecuritySystem.Status = 0; //deaktywacja czuwaka } } @@ -2623,7 +2623,7 @@ bool TMoverParameters::DecBrakeLevelOld(void) bool TMoverParameters::IncLocalBrakeLevel(int CtrlSpeed) { bool IBL; - if ((LocalBrakePos < LocalBrakePosNo) /*and (BrakeCtrlPos<1)*/) + if ((LocalBrakePos < LocalBrakePosNo) /*and (BrakeCtrlPos<1)*/) { while ((LocalBrakePos < LocalBrakePosNo) && (CtrlSpeed > 0)) { @@ -2777,34 +2777,49 @@ bool TMoverParameters::DynamicBrakeSwitch(bool Switch) // Q: 20160711 // włączenie / wyłączenie hamowania awaryjnego // ************************************************************************************************* -bool TMoverParameters::EmergencyBrakeSwitch(bool Switch) +bool TMoverParameters::RadiostopSwitch(bool Switch) { bool EBS; - if ((BrakeSystem != Individual) && (BrakeCtrlPosNo > 0)) - { - if ((!EmergencyBrakeFlag) && Switch) - { - EmergencyBrakeFlag = Switch; + if( ( BrakeSystem != Individual ) + && ( BrakeCtrlPosNo > 0 ) ) { + + if( ( true == Switch ) + && ( false == RadioStopFlag ) ) { + RadioStopFlag = Switch; EBS = true; } - else - { - if ((abs(V) < 0.1) && - (Switch == false)) // odblokowanie hamulca bezpieczenistwa tylko po zatrzymaniu - { - EmergencyBrakeFlag = Switch; + else { + if( ( Switch == false ) + && ( std::abs( V ) < 0.1 ) ) { + // odblokowanie hamulca bezpieczenistwa tylko po zatrzymaniu + RadioStopFlag = Switch; EBS = true; } - else + else { EBS = false; + } } } - else - EBS = false; // nie ma hamulca bezpieczenstwa gdy nie ma hamulca zesp. + else { + // nie ma hamulca bezpieczenstwa gdy nie ma hamulca zesp. + EBS = false; + } return EBS; } +bool TMoverParameters::AlarmChainSwitch( bool const State ) { + + bool stateswitched { false }; + + if( AlarmChainFlag != State ) { + // simple routine for the time being + AlarmChainFlag = State; + stateswitched = true; + } + return stateswitched; +} + // ************************************************************************************************* // Q: 20160710 // hamowanie przeciwpoślizgowe @@ -3282,12 +3297,19 @@ void TMoverParameters::UpdatePipePressure(double dt) Pipe2->Flow(dpMainValve); } - // if(EmergencyBrakeFlag)and(BrakeCtrlPosNo=0)then //ulepszony hamulec bezp. - if ((EmergencyBrakeFlag) || (TestFlag(SecuritySystem.Status, s_SHPebrake)) || - (TestFlag(SecuritySystem.Status, s_CAebrake)) || - (s_CAtestebrake == true) || - (TestFlag(EngDmgFlag, 32)) /* or (not Battery)*/) // ulepszony hamulec bezp. - dpMainValve = dpMainValve + PF(0, PipePress, 0.15) * dt; + // ulepszony hamulec bezp. + if( ( true == RadioStopFlag ) + || ( true == AlarmChainFlag ) + || ( true == TestFlag( SecuritySystem.Status, s_SHPebrake ) ) + || ( true == TestFlag( SecuritySystem.Status, s_CAebrake ) ) +/* + // NOTE: disabled because 32 is 'load destroyed' flag, what does this have to do with emergency brake? + // (if it's supposed to be broken coupler, such event sets alarmchainflag instead when appropriate) + || ( true == TestFlag( EngDmgFlag, 32 ) ) +*/ + || ( true == s_CAtestebrake ) ) { + dpMainValve = dpMainValve + PF( 0, PipePress, 0.15 ) * dt; + } // 0.2*Spg Pipe->Flow(-dpMainValve); Pipe->Flow(-(PipePress)*0.001 * dt); @@ -3766,6 +3788,41 @@ void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) //}; } +double TMoverParameters::BrakeForceR(double ratio, double velocity) +{ + double press = 0; + if (MBPM>2) + { + press = MaxBrakePress[1] + (MaxBrakePress[3] - MaxBrakePress[1]) * std::min(1.0, (TotalMass - Mass) / (MBPM - Mass)); + } + else + { + if (MaxBrakePress[1] > 0.1) + { + press = MaxBrakePress[LoadFlag]; + } + else + { + press = MaxBrakePress[3]; + if (DynamicBrakeType == dbrake_automatic) + ratio = ratio + (1.5 - ratio)*std::min(1.0, Vel*0.02); + if ((BrakeDelayFlag&bdelay_R) && (BrakeMethod%128 != bp_Cosid) && (BrakeMethod % 128 != bp_D1) && (BrakeMethod % 128 != bp_D2) && (Power<1) && (velocity<40)) + ratio = ratio / 2; + } + + } + return BrakeForceP(press*ratio, velocity); +} + +double TMoverParameters::BrakeForceP(double press, double velocity) +{ + double BFP = 0; + double K = (((press * P2FTrans) - BrakeCylSpring) * BrakeCylMult[0] - BrakeSlckAdj) * BrakeRigEff; + K *= static_cast(BrakeCylNo) / (NAxles * std::max(1, NBpA)); + BFP = Hamulec->GetFC(velocity, K)*K*(NAxles * std::max(1, NBpA)) * 1000; + return BFP; +} + // ************************************************************************************************* // Q: 20160713 // oblicza siłę na styku koła i szyny @@ -3809,7 +3866,7 @@ double TMoverParameters::BrakeForce(const TTrackParam &Track) Ntotal = u * BrakeRigEff; else { - u = (BrakePress * P2FTrans) * BrakeCylMult[0] - BrakeSlckAdj; + u = ((BrakePress * P2FTrans) - BrakeCylSpring) * BrakeCylMult[0] - BrakeSlckAdj; if (u * (2.0 - BrakeRigEff) < Ntotal) // histereza na nacisku klockow Ntotal = u * (2.0 - BrakeRigEff); } @@ -3863,6 +3920,9 @@ double TMoverParameters::FrictionForce(double R, int TDamage) return FF; } + + + // ************************************************************************************************* // Q: 20160713 // Oblicza przyczepność @@ -3971,17 +4031,17 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt) // blablabla // ABu: proby znalezienia problemu ze zle odbijajacymi sie skladami - //***if (Couplers[CouplerN].CouplingFlag=ctrain_virtual) and (newdist>0) then + //if (Couplers[CouplerN].CouplingFlag=ctrain_virtual) and (newdist>0) then if ((Couplers[CouplerN].CouplingFlag == ctrain_virtual) && (Couplers[CouplerN].CoupleDist > 0)) { CF = 0; // kontrola zderzania sie - OK ScanCounter++; if ((newdist > MaxDist) || ((ScanCounter > MaxCount) && (newdist > MinDist))) - //***if (tempdist>MaxDist) or ((ScanCounter>MaxCount)and(tempdist>MinDist)) then + //if (tempdist>MaxDist) or ((ScanCounter>MaxCount)and(tempdist>MinDist)) then { // zerwij kontrolnie wirtualny sprzeg // Connected.Couplers[CNext].Connected:=nil; //Ra: ten podłączony niekoniecznie jest // wirtualny - Couplers[CouplerN].Connected = NULL; +// Couplers[CouplerN].Connected = NULL; ScanCounter = static_cast(Random(500.0)); // Q: TODO: cy dobrze przetlumaczone? // WriteLog(FloatToStr(ScanCounter)); } @@ -4612,36 +4672,26 @@ double TMoverParameters::TractionForce(double dt) else tmp = 4; // szybkie malenie, powolne wzrastanie } - // if SlippingWheels then begin PosRatio:=0; tmp:=10; SandDoseOn; - // end;//przeciwposlizg - - // if(Flat)then //PRZECIWPOŚLIZG dmoment = eimv[eimv_Fful]; - // else - // dmoment:=eimc[eimc_p_F0]*0.99; // NOTE: the commands to operate the sandbox are likely to conflict with other similar ai decisions // TODO: gather these in single place so they can be resolved together - if ((abs((PosRatio + 9.66 * dizel_fill) * dmoment * 100) > - 0.95 * Adhesive(RunningTrack.friction) * TotalMassxg)) - { + if( ( std::abs( ( PosRatio + 9.66 * dizel_fill ) * dmoment * 100 ) > 0.95 * Adhesive( RunningTrack.friction ) * TotalMassxg ) ) { PosRatio = 0; tmp = 4; - Sandbox( true, range::local ); } // przeciwposlizg - if ((abs((PosRatio + 9.80 * dizel_fill) * dmoment * 100) > - 0.95 * Adhesive(RunningTrack.friction) * TotalMassxg)) - { + if( ( std::abs( ( PosRatio + 9.80 * dizel_fill ) * dmoment * 100 ) > 0.95 * Adhesive( RunningTrack.friction ) * TotalMassxg ) ) { + PosRatio = 0; + tmp = 9; + } // przeciwposlizg + if( ( SlippingWheels ) ) { PosRatio = 0; tmp = 9; Sandbox( true, range::local ); } // przeciwposlizg - if ((SlippingWheels)) - { - // PosRatio = -PosRatio * 0; // serio -0 ??? - PosRatio = 0; - tmp = 9; - Sandbox( true, range::local ); - } // przeciwposlizg + else { + // switch sandbox off + Sandbox( false, range::local ); + } dizel_fill += Max0R(Min0R(PosRatio - dizel_fill, 0.1), -0.1) * 2 * (tmp /*2{+4*byte(PosRatio 0) - { - if ((Flag & Value) == 0) - { + if( Value > 0 ) { + if( false == TestFlag( Flag, Value ) ) { Flag |= Value; return true; // true, gdy było wcześniej 0 i zostało ustawione } } - else if (Value < 0) - { - Value = abs(Value); - if ((Flag & Value) == Value) - { - Flag &= ~Value; // Value jest ujemne, czyli zerowanie flagi - return true; // true, gdy było wcześniej 1 i zostało wyzerowane - } + else if( Value < 0 ) { + // Value jest ujemne, czyli zerowanie flagi + return ClearFlag( Flag, -Value ); } - return false; + return false; } -bool UnSetFlag(int &Flag, int Value) -{ - Flag &= ~Value; - return true; +bool ClearFlag( int &Flag, int const Value ) { + + if( true == TestFlag( Flag, Value ) ) { + Flag &= ~Value; + return true; + } + else { + return false; + } } inline double Random(double a, double b) @@ -124,14 +114,14 @@ std::string DWE(std::string s) /*Delete After Equal sign*/ return s; } -std::string ExchangeCharInString( std::string const &Source, char const &From, char const &To ) +std::string ExchangeCharInString( std::string const &Source, char const From, char const To ) { std::string replacement; replacement.reserve( Source.size() ); - std::for_each(Source.cbegin(), Source.cend(), [&](char const idx) { - if( idx != From ) { replacement += idx; } - else { - if( To != NULL ) { replacement += To; } } - } ); + std::for_each( + std::begin( Source ), std::end( Source ), + [&](char const idx) { + if( idx != From ) { replacement += idx; } + else { replacement += To; } } ); return replacement; } diff --git a/McZapkie/mctools.h b/McZapkie/mctools.h index 5d24be27..75a263fb 100644 --- a/McZapkie/mctools.h +++ b/McZapkie/mctools.h @@ -63,9 +63,9 @@ inline double BorlandTime() std::string Now(); /*funkcje logiczne*/ -bool TestFlag(int Flag, int Value); -bool SetFlag( int & Flag, int Value); -bool UnSetFlag(int &Flag, int Value); +inline bool TestFlag( int const Flag, int const Value ) { return ( ( Flag & Value ) == Value ); } +bool SetFlag( int &Flag, int const Value); +bool ClearFlag(int &Flag, int const Value); bool FuzzyLogic(double Test, double Threshold, double Probability); /*jesli Test>Threshold to losowanie*/ @@ -75,7 +75,7 @@ bool FuzzyLogicAI(double Test, double Threshold, double Probability); /*operacje na stringach*/ std::string DUE(std::string s); /*Delete Until Equal sign*/ std::string DWE(std::string s); /*Delete While Equal sign*/ -std::string ExchangeCharInString(std::string const &s, const char &aim, const char &target); // zamienia jeden znak na drugi +std::string ExchangeCharInString( std::string const &Source, char const From, char const To ); // zamienia jeden znak na drugi std::vector &Split(const std::string &s, char delim, std::vector &elems); std::vector Split(const std::string &s, char delim); //std::vector Split(const std::string &s); diff --git a/Model3d.cpp b/Model3d.cpp index e9f3a8fc..41834753 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -28,7 +28,7 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others using namespace Mtable; -double TSubModel::fSquareDist = 0; +float TSubModel::fSquareDist = 0.f; size_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu texture_handle const *TSubModel::ReplacableSkinId = NULL; int TSubModel::iAlpha = 0x30300030; // maska do testowania flag tekstur wymiennych @@ -63,16 +63,16 @@ TSubModel::~TSubModel() // wyświetlania }; -void TSubModel::TextureNameSet(std::string const &Name) +void TSubModel::Name_Material(std::string const &Name) { // ustawienie nazwy submodelu, o // ile nie jest wczytany z E3D if (iFlags & 0x0200) { // tylko jeżeli submodel zosta utworzony przez new - pTexture = Name; + m_materialname = Name; } }; -void TSubModel::NameSet(std::string const &Name) +void TSubModel::Name(std::string const &Name) { // ustawienie nazwy submodelu, o ile // nie jest wczytany z E3D if (iFlags & 0x0200) @@ -177,7 +177,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic std::string token; parser.getTokens(1, false); // nazwa submodelu bez zmieny na małe parser >> token; - NameSet(token); + Name(token); if (dynamic) { // dla pojazdu, blokujemy załączone submodele, które mogą być nieobsługiwane if( ( token.size() >= 3 ) @@ -305,50 +305,51 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic */ if (!parser.expectToken("map:")) Error("Model map parse failure!"); - std::string texture = parser.getToken(); - if (texture == "none") + std::string material = parser.getToken(); + if (material == "none") { // rysowanie podanym kolorem - TextureID = 0; + m_material = null_handle; iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych } - else if (texture.find("replacableskin") != texture.npos) + else if (material.find("replacableskin") != material.npos) { // McZapkie-060702: zmienialne skory modelu - TextureID = -1; + m_material = -1; iFlags |= (Opacity < 1.0) ? 1 : 0x10; // zmienna tekstura 1 } - else if (texture == "-1") + else if (material == "-1") { - TextureID = -1; + m_material = -1; iFlags |= (Opacity < 1.0) ? 1 : 0x10; // zmienna tekstura 1 } - else if (texture == "-2") + else if (material == "-2") { - TextureID = -2; + m_material = -2; iFlags |= (Opacity < 1.0) ? 2 : 0x10; // zmienna tekstura 2 } - else if (texture == "-3") + else if (material == "-3") { - TextureID = -3; + m_material = -3; iFlags |= (Opacity < 1.0) ? 4 : 0x10; // zmienna tekstura 3 } - else if (texture == "-4") + else if (material == "-4") { - TextureID = -4; + m_material = -4; iFlags |= (Opacity < 1.0) ? 8 : 0x10; // zmienna tekstura 4 } else { // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur - TextureNameSet(texture); - if( texture.find_first_of( "/\\" ) == texture.npos ) { - texture.insert( 0, Global::asCurrentTexturePath ); + Name_Material(material); + if( material.find_first_of( "/\\" ) == material.npos ) { + material.insert( 0, Global::asCurrentTexturePath ); } - TextureID = GfxRenderer.Fetch_Texture( texture ); + m_material = GfxRenderer.Fetch_Material( material ); // renderowanie w cyklu przezroczystych tylko jeśli: // 1. Opacity=0 (przejściowo <1, czy tam <100) oraz // 2. tekstura ma przezroczystość iFlags |= ( ( ( Opacity < 1.0 ) - && ( GfxRenderer.Texture(TextureID).has_alpha ) ) ? + && ( ( m_material != null_handle ) + && ( GfxRenderer.Material( m_material ).has_alpha ) ) ) ? 0x20 : 0x10 ); // 0x10-nieprzezroczysta, 0x20-przezroczysta }; @@ -591,10 +592,10 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic return iNumVerts; // do określenia wielkości VBO }; -int TSubModel::TriangleAdd(TModel3d *m, texture_handle tex, int tri) +int TSubModel::TriangleAdd(TModel3d *m, material_handle tex, int tri) { // dodanie trójkątów do submodelu, używane przy tworzeniu E3D terenu TSubModel *s = this; - while (s ? (s->TextureID != tex) : false) + while (s ? (s->m_material != tex) : false) { // szukanie submodelu o danej teksturze if (s == this) s = Child; @@ -603,15 +604,15 @@ int TSubModel::TriangleAdd(TModel3d *m, texture_handle tex, int tri) } if (!s) { - if (TextureID <= 0) + if (m_material <= 0) s = this; // użycie głównego else { // dodanie nowego submodelu do listy potomnych s = new TSubModel(); m->AddTo(this, s); } - s->TextureNameSet(GfxRenderer.Texture(tex).name); - s->TextureID = tex; + s->Name_Material(GfxRenderer.Material(tex).name); + s->m_material = tex; s->eType = GL_TRIANGLES; } if (s->iNumVerts < 0) @@ -644,72 +645,6 @@ basic_vertex *TSubModel::TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, return s->Vertices + pos; // wskaźnik na wolne miejsce w tabeli wierzchołków }; */ -#ifdef EU07_USE_OLD_RENDERCODE -void TSubModel::DisplayLists() -{ // utworznie po jednej skompilowanej liście dla - // każdego submodelu - if (Global::bUseVBO) - return; // Ra: przy VBO to się nie przyda - if (eType < TP_ROTATOR) - { - if (iNumVerts > 0) - { - uiDisplayList = glGenLists(1); - glNewList(uiDisplayList, GL_COMPILE); -#ifdef USE_VERTEX_ARRAYS - // ShaXbee-121209: przekazywanie wierzcholkow hurtem - glVertexPointer(3, GL_DOUBLE, sizeof(GLVERTEX), &Vertices[0].Point.x); - glNormalPointer(GL_DOUBLE, sizeof(GLVERTEX), &Vertices[0].Normal.x); - glTexCoordPointer(2, GL_FLOAT, sizeof(GLVERTEX), &Vertices[0].tu); - glDrawArrays(eType, 0, iNumVerts); -#else - glBegin(eType); - for (int i = 0; i < iNumVerts; i++) - { - /* - glNormal3dv(&Vertices[i].Normal.x); - glTexCoord2f(Vertices[i].tu,Vertices[i].tv); - glVertex3dv(&Vertices[i].Point.x); - */ - glNormal3fv(glm::value_ptr(Vertices[i].normal)); - glTexCoord2fv(glm::value_ptr(Vertices[i].texture)); - glVertex3fv(glm::value_ptr(Vertices[i].position)); - }; - glEnd(); -#endif - glEndList(); - } - } - else if (eType == TP_FREESPOTLIGHT) - { - uiDisplayList = glGenLists(1); - glNewList(uiDisplayList, GL_COMPILE); - glBegin(GL_POINTS); - glVertex3f( 0.0f, 0.0f, -0.05f ); // shift point towards the viewer, to avoid z-fighting with the light polygons - glEnd(); - glEndList(); - } - else if (eType == TP_STARS) - { // punkty świecące dookólnie - uiDisplayList = glGenLists(1); - glNewList(uiDisplayList, GL_COMPILE); - glBegin(GL_POINTS); - for (int i = 0; i < iNumVerts; ++i) - { - glColor3fv(glm::value_ptr(Vertices[i].normal)); - glVertex3fv(glm::value_ptr(Vertices[i].position)); - }; - glEnd(); - glEndList(); - } - // SafeDeleteArray(Vertices); //przy VBO muszą zostać do załadowania całego - // modelu - if (Child) - Child->DisplayLists(); - if (Next) - Next->DisplayLists(); -}; -#endif void TSubModel::InitialRotate(bool doit) { // konwersja układu współrzędnych na zgodny ze scenerią @@ -806,8 +741,8 @@ int TSubModel::FlagsCheck() int i = 0; if (Child) { // Child jest renderowany po danym submodelu - if (Child->TextureID) // o ile ma teksturę - if (Child->TextureID != TextureID) // i jest ona inna niż rodzica + if (Child->m_material) // o ile ma teksturę + if (Child->m_material != m_material) // i jest ona inna niż rodzica Child->iFlags |= 0x80; // to trzeba sprawdzać, jak z teksturami jest i = Child->FlagsCheck(); iFlags |= 0x00FF0000 & ((i << 16) | (i) | (i >> 8)); // potomny, rodzeństwo i dzieci @@ -825,8 +760,8 @@ int TSubModel::FlagsCheck() if (Next) { // Next jest renderowany po danym submodelu (kolejność odwrócona // po wczytaniu T3D) - if (TextureID) // o ile dany ma teksturę - if ((TextureID != Next->TextureID) || + if (m_material) // o ile dany ma teksturę + if ((m_material != Next->m_material) || (i & 0x00800000)) // a ma inną albo dzieci zmieniają iFlags |= 0x80; // to dany submodel musi sobie ją ustawiać i = Next->FlagsCheck(); @@ -907,23 +842,18 @@ struct ToLower }; TSubModel *TSubModel::GetFromName(std::string const &search, bool i) -{ - return GetFromName(search.c_str(), i); -}; - -TSubModel *TSubModel::GetFromName(char const *search, bool i) { TSubModel *result; // std::transform(search.begin(),search.end(),search.begin(),ToLower()); // search=search.LowerCase(); // AnsiString name=AnsiString(); - std::string search_lc = std::string(search); + std::string search_lc = search; if (i) std::transform(search_lc.begin(), search_lc.end(), search_lc.begin(), ::tolower); std::string pName_lc = pName; if (i) std::transform(pName_lc.begin(), pName_lc.end(), pName_lc.begin(), ::tolower); - if (pName.size() && search) + if (pName.size() && search.size()) if (pName_lc == search_lc) return this; if (Next) @@ -1043,7 +973,7 @@ void TSubModel::serialize_geometry( std::ostream &Output ) const { if( Child ) { Child->serialize_geometry( Output ); } - if( m_geometry != NULL ) { + if( m_geometry != null_handle ) { for( auto const &vertex : GfxRenderer.Vertices( m_geometry ) ) { vertex.serialize( Output ); } @@ -1085,13 +1015,13 @@ TSubModel::convert( TGroundNode &Groundnode ) const { Groundnode.Ambient = f4Ambient; Groundnode.Diffuse = f4Diffuse; Groundnode.Specular = f4Specular; - Groundnode.TextureID = TextureID; + Groundnode.m_material = m_material; Groundnode.iFlags = ( - ( true == GfxRenderer.Texture( TextureID ).has_alpha ) ? + ( true == GfxRenderer.Material( m_material ).has_alpha ) ? 0x20 : 0x10 ); - if( m_geometry == NULL ) { return; } + if( m_geometry == null_handle ) { return; } std::size_t vertexcount { 0 }; std::vector importedvertices; @@ -1136,23 +1066,6 @@ TSubModel::convert( TGroundNode &Groundnode ) const { } } -// NOTE: leftover from static distance factor adjustment. -// TODO: get rid of it, once we have the dynamic adjustment code in place -void TSubModel::AdjustDist() -{ // aktualizacja odległości faz LoD, zależna od - // rozdzielczości pionowej oraz multisamplingu - if (fSquareMaxDist > 0.0) - fSquareMaxDist *= Global::fDistanceFactor; - if (fSquareMinDist > 0.0) - fSquareMinDist /= Global::fDistanceFactor; - // if (fNearAttenStart>0.0) fNearAttenStart*=Global::fDistanceFactor; - // if (fNearAttenEnd>0.0) fNearAttenEnd*=Global::fDistanceFactor; - if (Child) - Child->AdjustDist(); - if (Next) - Next->AdjustDist(); -}; - void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) { // ustawienie kolorów dla modelu terenu f4Ambient = glm::vec4( Ambient, 1.0f ); @@ -1197,7 +1110,7 @@ float TSubModel::MaxY( float4x4 const &m ) { auto maxy { 0.0f }; // binary and text models invoke this function at different stages, either after or before geometry data was sent to the geometry manager - if( m_geometry != NULL ) { + if( m_geometry != null_handle ) { for( auto const &vertex : GfxRenderer.Vertices( m_geometry ) ) { maxy = std::max( @@ -1229,23 +1142,19 @@ TModel3d::TModel3d() Root = NULL; iFlags = 0; iSubModelsCount = 0; - iModel = NULL; // tylko jak wczytany model binarny iNumVerts = 0; // nie ma jeszcze wierzchołków }; -TModel3d::~TModel3d() -{ - // SafeDeleteArray(Materials); - if (iFlags & 0x0200) - { // wczytany z pliku tekstowego, submodele sprzątają same - SafeDelete(Root); // submodele się usuną rekurencyjnie +TModel3d::~TModel3d() { + + if (iFlags & 0x0200) { + // wczytany z pliku tekstowego, submodele sprzątają same + Root = nullptr; } - else - { // wczytano z pliku binarnego (jest właścicielem tablic) - Root = nullptr; - delete[] iModel; // usuwamy cały wczytany plik i to wystarczy - } - // później się jeszcze usuwa obiekt z którego dziedziczymy tabelę VBO + else { + // wczytano z pliku binarnego (jest właścicielem tablic) + SafeDeleteArray( Root ); // submodele się usuną rekurencyjnie + } }; TSubModel *TModel3d::AddToNamed(const char *Name, TSubModel *SubModel) @@ -1272,16 +1181,16 @@ void TModel3d::AddTo(TSubModel *tmp, TSubModel *SubModel) iFlags |= 0x0200; // submodele są oddzielne }; -TSubModel *TModel3d::GetFromName(const char *sName) +TSubModel *TModel3d::GetFromName(std::string const &Name) { // wyszukanie submodelu po nazwie - if (!sName) + if (Name.empty()) return Root; // potrzebne do terenu z E3D if (iFlags & 0x0200) // wczytany z pliku tekstowego, wyszukiwanie rekurencyjne - return Root ? Root->GetFromName(sName) : nullptr; + return Root ? Root->GetFromName(Name) : nullptr; else // wczytano z pliku binarnego, można wyszukać iteracyjnie { // for (int i=0;iGetFromName(sName) : nullptr; + return Root ? Root->GetFromName(Name) : nullptr; } }; @@ -1379,10 +1288,10 @@ void TSubModel::serialize(std::ostream &s, sn_utils::ls_int32(s, iNumVerts); sn_utils::ls_int32(s, tVboPtr); - if (TextureID <= 0) - sn_utils::ls_int32(s, TextureID); + if (m_material <= 0) + sn_utils::ls_int32(s, m_material); else - sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, pTexture)); + sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname)); sn_utils::ls_float32(s, fVisible); sn_utils::ls_float32(s, fLight); @@ -1433,16 +1342,16 @@ void TModel3d::SaveToBinFile(std::string const &FileName) std::ofstream s(FileName, std::ios::binary); sn_utils::ls_uint32(s, MAKE_ID4('E', '3', 'D', '0')); - size_t e3d_spos = s.tellp(); + auto const e3d_spos = s.tellp(); sn_utils::ls_uint32(s, 0); { sn_utils::ls_uint32(s, MAKE_ID4('S', 'U', 'B', '0')); - size_t sub_spos = s.tellp(); + auto const sub_spos = s.tellp(); sn_utils::ls_uint32(s, 0); for (size_t i = 0; i < models.size(); i++) models[i]->serialize(s, models, names, textures, transforms); - size_t pos = s.tellp(); + auto const pos = s.tellp(); s.seekp(sub_spos); sn_utils::ls_uint32(s, (uint32_t)(4 + pos - sub_spos)); s.seekp(pos); @@ -1460,11 +1369,11 @@ void TModel3d::SaveToBinFile(std::string const &FileName) if (textures.size()) { sn_utils::ls_uint32(s, MAKE_ID4('T', 'E', 'X', '0')); - size_t tex_spos = s.tellp(); + auto const tex_spos = s.tellp(); sn_utils::ls_uint32(s, 0); for (size_t i = 0; i < textures.size(); i++) sn_utils::s_str(s, textures[i]); - size_t pos = s.tellp(); + auto const pos = s.tellp(); s.seekp(tex_spos); sn_utils::ls_uint32(s, (uint32_t)(4 + pos - tex_spos)); s.seekp(pos); @@ -1473,17 +1382,17 @@ void TModel3d::SaveToBinFile(std::string const &FileName) if (names.size()) { sn_utils::ls_uint32(s, MAKE_ID4('N', 'A', 'M', '0')); - size_t nam_spos = s.tellp(); + auto const nam_spos = s.tellp(); sn_utils::ls_uint32(s, 0); for (size_t i = 0; i < names.size(); i++) sn_utils::s_str(s, names[i]); - size_t pos = s.tellp(); + auto const pos = s.tellp(); s.seekp(nam_spos); sn_utils::ls_uint32(s, (uint32_t)(4 + pos - nam_spos)); s.seekp(pos); } - size_t end = s.tellp(); + auto const end = s.tellp(); s.seekp(e3d_spos); sn_utils::ls_uint32(s, (uint32_t)(4 + end - e3d_spos)); s.close(); @@ -1537,8 +1446,7 @@ void TSubModel::deserialize(std::istream &s) void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) { Root = nullptr; - float4x4 *tm = nullptr; - if( m_geometrybank == NULL ) { + if( m_geometrybank == null_handle ) { m_geometrybank = GfxRenderer.Create_Bank(); } @@ -1560,7 +1468,7 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) iSubModelsCount = (int)sm_cnt; Root = new TSubModel[sm_cnt]; size_t pos = s.tellg(); - for (size_t i = 0; i < sm_cnt; i++) + for (size_t i = 0; i < sm_cnt; ++i) { s.seekg(pos + sm_size * i); Root[i].deserialize(s); @@ -1575,18 +1483,13 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) size_t vt_cnt = size / 32; iNumVerts = (int)vt_cnt; m_nVertexCount = (int)vt_cnt; -#ifdef EU07_USE_OLD_VERTEXBUFFER - assert( m_pVNT == nullptr ); - m_pVNT = new basic_vertex[vt_cnt]; -#else m_pVNT.resize( vt_cnt ); -#endif for (size_t i = 0; i < vt_cnt; i++) m_pVNT[i].deserialize(s); */ - // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk + // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk. // geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first - std::vector< std::pair > submodeloffsets; + std::vector< std::pair > submodeloffsets; // vertex data offset, submodel index submodeloffsets.reserve( iSubModelsCount ); for( int submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) { auto const &submodel = Root[ submodelindex ]; @@ -1633,23 +1536,23 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) } else if (type == MAKE_ID4('T', 'R', 'A', '0')) { - if (tm != nullptr) - throw std::runtime_error("e3d: duplicated TRA chunk"); + if( false == Matrices.empty() ) + throw std::runtime_error("e3d: duplicated TRA chunk"); size_t t_cnt = size / 64; - tm = new float4x4[t_cnt]; - for (size_t i = 0; i < t_cnt; i++) - tm[i].deserialize_float32(s); + Matrices.resize(t_cnt); + for (size_t i = 0; i < t_cnt; ++i) + Matrices[i].deserialize_float32(s); } else if (type == MAKE_ID4('T', 'R', 'A', '1')) { - if (tm != nullptr) - throw std::runtime_error("e3d: duplicated TRA chunk"); + if( false == Matrices.empty() ) + throw std::runtime_error("e3d: duplicated TRA chunk"); size_t t_cnt = size / 128; - tm = new float4x4[t_cnt]; - for (size_t i = 0; i < t_cnt; i++) - tm[i].deserialize_float64(s); + Matrices.resize( t_cnt ); + for (size_t i = 0; i < t_cnt; ++i) + Matrices[i].deserialize_float64(s); } else if (type == MAKE_ID4('T', 'E', 'X', '0')) { @@ -1671,17 +1574,10 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) if (!Root) throw std::runtime_error("e3d: no submodels"); -/* -#ifdef EU07_USE_OLD_VERTEXBUFFER - if (!m_pVNT) -#else - if(m_pVNT.empty() ) -#endif - throw std::runtime_error("e3d: no vertices"); -*/ - for (size_t i = 0; (int)i < iSubModelsCount; ++i) + + for (size_t i = 0; (int)i < iSubModelsCount; ++i) { - Root[i].BinInit( Root, tm, &Textures, &Names, dynamic ); + Root[i].BinInit( Root, Matrices.data(), &Textures, &Names, dynamic ); if (Root[i].ChildGet()) Root[i].ChildGet()->Parent = &Root[i]; @@ -1727,26 +1623,29 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, pName = ""; if (iTexture > 0) { // obsługa stałej tekstury - if( iTexture < t->size() ) { - pTexture = t->at( iTexture ); - if( pTexture.find_last_of( "/\\" ) == std::string::npos ) - pTexture.insert( 0, Global::asCurrentTexturePath ); - TextureID = GfxRenderer.Fetch_Texture( pTexture ); + auto const materialindex = static_cast( iTexture ); + if( materialindex < t->size() ) { + m_materialname = t->at( materialindex ); + if( m_materialname.find_last_of( "/\\" ) == std::string::npos ) { + m_materialname = Global::asCurrentTexturePath + m_materialname; + } + m_material = GfxRenderer.Fetch_Material( m_materialname ); if( ( iFlags & 0x30 ) == 0 ) { // texture-alpha based fallback if for some reason we don't have opacity flag set yet - iFlags |= - ( GfxRenderer.Texture( TextureID ).has_alpha ? + iFlags |= ( + ( ( m_material != null_handle ) + && ( GfxRenderer.Material( m_material ).has_alpha ) ) ? 0x20 : 0x10 ); // 0x10-nieprzezroczysta, 0x20-przezroczysta } } else { ErrorLog( "Bad model: reference to non-existent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) ); - TextureID = NULL; + m_material = null_handle; } } else - TextureID = iTexture; + m_material = iTexture; b_aAnim = b_Anim; // skopiowanie animacji do drugiego cyklu @@ -1874,7 +1773,7 @@ void TModel3d::Init() } iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu if (iNumVerts) { - if( m_geometrybank == NULL ) { + if( m_geometrybank == null_handle ) { m_geometrybank = GfxRenderer.Create_Bank(); } std::size_t dataoffset = 0; diff --git a/Model3d.h b/Model3d.h index 4e72b3c4..181bd93a 100644 --- a/Model3d.h +++ b/Model3d.h @@ -14,7 +14,7 @@ http://mozilla.org/MPL/2.0/. #include "dumb3d.h" #include "Float3d.h" #include "VBO.h" -#include "Texture.h" +#include "material.h" using namespace Math3D; @@ -68,8 +68,8 @@ public: }; private: - int iNext{ NULL }; - int iChild{ NULL }; + int iNext{ 0 }; + int iChild{ 0 }; int eType{ TP_ROTATOR }; // Ra: modele binarne dają więcej możliwości niż mesh złożony z trójkątów int iName{ -1 }; // numer łańcucha z nazwą submodelu, albo -1 gdy anonimowy public: // chwilowo @@ -121,8 +121,8 @@ private: TSubModel *Next { nullptr }; TSubModel *Child { nullptr }; - geometry_handle m_geometry { NULL, NULL }; // geometry of the submodel - texture_handle TextureID { NULL }; // numer tekstury, -1 wymienna, 0 brak + geometry_handle m_geometry { 0, 0 }; // geometry of the submodel + material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak bool bWire { false }; // nie używane, ale wczytywane float Opacity { 1.0f }; float f_Angle { 0.0f }; @@ -135,7 +135,7 @@ public: // chwilowo basic_vertex *Vertices; // roboczy wskaźnik - wczytanie T3D do VBO */ vertex_array Vertices; - size_t iAnimOwner{ NULL }; // roboczy numer egzemplarza, który ustawił animację + size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane public: float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) @@ -143,7 +143,7 @@ public: TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy int iVisible{ 1 }; // roboczy stan widoczności - std::string pTexture; // robocza nazwa tekstury do zapisania w pliku binarnym + std::string m_materialname; // robocza nazwa tekstury do zapisania w pliku binarnym std::string pName; // robocza nazwa private: int SeekFaceNormal( std::vector const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, vertex_array const &Vertices ); @@ -151,9 +151,9 @@ private: public: static size_t iInstance; // identyfikator egzemplarza, który aktualnie renderuje model - static texture_handle const *ReplacableSkinId; + static material_handle const *ReplacableSkinId; static int iAlpha; // maska bitowa dla danego przebiegu - static double fSquareDist; + static float fSquareDist; static TModel3d *pRoot; static std::string *pasText; // tekst dla wyświetlacza (!!!! do przemyślenia) ~TSubModel(); @@ -162,7 +162,7 @@ public: void NextAdd(TSubModel *SubModel); TSubModel * NextGet() { return Next; }; TSubModel * ChildGet() { return Child; }; - int TriangleAdd(TModel3d *m, texture_handle tex, int tri); + int TriangleAdd(TModel3d *m, material_handle tex, int tri); /* basic_vertex * TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ); */ @@ -172,8 +172,7 @@ public: void SetTranslate(vector3 vNewTransVector); void SetTranslate(float3 vNewTransVector); void SetRotateIK1(float3 vNewAngles); - TSubModel * GetFromName(std::string const &search, bool i = true); - TSubModel * GetFromName(char const *search, bool i = true); + TSubModel * GetFromName( std::string const &search, bool i = true ); inline float4x4 * GetMatrix() { return fMatrix; }; inline void Hide() { iVisible = 0; }; @@ -186,13 +185,13 @@ public: }; void InitialRotate(bool doit); void BinInit(TSubModel *s, float4x4 *m, std::vector *t, std::vector *n, bool dynamic); - void ReplacableSet(texture_handle const *r, int a) + void ReplacableSet(material_handle const *r, int a) { ReplacableSkinId = r; iAlpha = a; }; - void TextureNameSet( std::string const &Name ); - void NameSet( std::string const &Name ); + void Name_Material( std::string const &Name ); + void Name( std::string const &Name ); // Ra: funkcje do budowania terenu z E3D int Flags() { return iFlags; }; void UnFlagNext() { iFlags &= 0x00FFFFFF; }; @@ -203,11 +202,10 @@ public: return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector; } inline float3 Translation2Get() { return *(fMatrix->TranslationGet()) + Child->Translation1Get(); } - int GetTextureId() { - return TextureID; } + material_handle GetMaterial() { + return m_material; } void ParentMatrix(float4x4 *m); float MaxY( float4x4 const &m ); - void AdjustDist(); void deserialize(std::istream&); void serialize(std::ostream&, @@ -233,7 +231,7 @@ public: // Ra: tymczasowo private: std::vector Textures; // nazwy tekstur std::vector Names; // nazwy submodeli - int *iModel; // zawartość pliku binarnego + std::vector Matrices; // submodel matrices int iSubModelsCount; // Ra: używane do tworzenia binarnych std::string asBinary; // nazwa pod którą zapisać model binarny std::string m_filename; @@ -241,7 +239,7 @@ public: inline TSubModel * GetSMRoot() { return (Root); }; TModel3d(); ~TModel3d(); - TSubModel * GetFromName(const char *sName); + TSubModel * GetFromName(std::string const &Name); TSubModel * AddToNamed(const char *Name, TSubModel *SubModel); void AddTo(TSubModel *tmp, TSubModel *SubModel); void LoadFromTextFile(std::string const &FileName, bool dynamic); diff --git a/PyInt.cpp b/PyInt.cpp index 8a6a74b4..6d91a24b 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -291,21 +291,16 @@ void TPythonScreenRenderer::updateTexture() sprintf(buff, "Sending texture id: %d w: %d h: %d", _textureId, width, height); WriteLog(buff); #endif // _PY_INT_MORE_LOG - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - GfxRenderer.Bind(_textureId); +/* + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glPixelStorei( GL_PACK_ALIGNMENT, 1 ); +*/ + GfxRenderer.Bind_Material(_textureId); // setup texture parameters - if( GLEW_VERSION_1_4 ) { - - glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 ); - } - else { - - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - } + glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 ); // build texture glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData ); @@ -451,13 +446,13 @@ void TPythonScreens::init(cParser &parser, TModel3d *model, std::string const &n >> asPyClassName; std::string subModelName = ToLower( asSubModelName ); std::string pyClassName = ToLower( asPyClassName ); - TSubModel *subModel = model->GetFromName(subModelName.c_str()); + TSubModel *subModel = model->GetFromName(subModelName); if (subModel == NULL) { WriteLog( "Python Screen: submodel " + subModelName + " not found - Ignoring screen" ); return; // nie ma takiego sub modelu w danej kabinie pomijamy } - auto textureId = subModel->GetTextureId(); + auto textureId = subModel->GetMaterial(); if (textureId <= 0) { WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" ); diff --git a/Segment.cpp b/Segment.cpp index ea9a8e6b..19a0fa15 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -454,7 +454,7 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, void TSegment::Render() { vector3 pt; - GfxRenderer.Bind(0); + GfxRenderer.Bind_Material( null_handle ); if (bCurve) { diff --git a/Sound.cpp b/Sound.cpp index c3101245..9c026893 100644 --- a/Sound.cpp +++ b/Sound.cpp @@ -224,7 +224,7 @@ void TSoundsManager::RestoreAll() hr = Next->DSBuffer->Restore(); if (hr == DSERR_BUFFERLOST) Sleep(10); - } while ((hr = Next->DSBuffer->Restore()) != NULL); + } while ((hr = Next->DSBuffer->Restore()) != 0); // char *Name= Next->Name; // int cc= Next->Concurrent; @@ -237,69 +237,21 @@ void TSoundsManager::RestoreAll() }; }; -void TSoundsManager::Init(HWND hWnd) -{ +bool TSoundsManager::Init(HWND hWnd) { - First = NULL; + First = nullptr; Count = 0; - pDS = NULL; - pDSNotify = NULL; - - HRESULT hr; //=222; - LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; - - // strcpy(Directory, NDirectory); + pDS = nullptr; + pDSNotify = nullptr; // Create IDirectSound using the primary sound device - hr = DirectSoundCreate(NULL, &pDS, NULL); - if (hr != DS_OK) - { + auto hr = ::DirectSoundCreate(NULL, &pDS, NULL); + if (hr != DS_OK) { - if (hr == DSERR_ALLOCATED) - return; - if (hr == DSERR_INVALIDPARAM) - return; - if (hr == DSERR_NOAGGREGATION) - return; - if (hr == DSERR_NODRIVER) - return; - if (hr == DSERR_OUTOFMEMORY) - return; - - // hr=0; + return false; }; - // return ; - // Set coop level to DSSCL_PRIORITY - // if( FAILED( hr = pDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) ) ); - // return ; - pDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY); + pDS->SetCooperativeLevel(hWnd, DSSCL_NORMAL); - // Get the primary buffer - DSBUFFERDESC dsbd; - ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); - dsbd.dwSize = sizeof(DSBUFFERDESC); - dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; - if (!Global::bInactivePause) // jeśli przełączony w tło ma nadal działać - dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS; // to dźwięki mają być również słyszalne - dsbd.dwBufferBytes = 0; - dsbd.lpwfxFormat = NULL; - - if (FAILED(hr = pDS->CreateSoundBuffer(&dsbd, &pDSBPrimary, NULL))) - return; - - // Set primary buffer format to 22kHz and 16-bit output. - WAVEFORMATEX wfx; - ZeroMemory(&wfx, sizeof(WAVEFORMATEX)); - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = 2; - wfx.nSamplesPerSec = 44100; - wfx.wBitsPerSample = 16; - wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - - if (FAILED(hr = pDSBPrimary->SetFormat(&wfx))) - return; - - SAFE_RELEASE(pDSBPrimary); -}; \ No newline at end of file + return true; +}; diff --git a/Sound.h b/Sound.h index 6b77d184..393d7dad 100644 --- a/Sound.h +++ b/Sound.h @@ -43,7 +43,7 @@ private: public: ~TSoundsManager(); - static void Init( HWND hWnd ); + static bool Init( HWND hWnd ); static void Free(); static LPDIRECTSOUNDBUFFER GetFromName( std::string const &Name, bool Dynamic, float *fSamplingRate = NULL ); static void RestoreAll(); diff --git a/Texture.cpp b/Texture.cpp index ce557936..f2a294f4 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -506,18 +506,15 @@ opengl_texture::load_TGA() { return; } -resource_state +bool opengl_texture::bind() { - if( false == is_ready ) { - - create(); - if( data_state != resource_state::good ) { - return data_state; - } + if( ( false == is_ready ) + && ( false == create() ) ) { + return false; } ::glBindTexture( GL_TEXTURE_2D, id ); - return data_state; + return true; } bool @@ -645,40 +642,7 @@ opengl_texture::set_filtering() { switch( trait ) { case '#': { sharpen = true; break; } -/* - // legacy filter modes. TODO, TBD: get rid of them? - // let's just turn them off and see if anyone notices. - case '4': { - // najbliższy z tekstury - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - break; - } - case '5': { - //średnia z tekstury - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - break; - } - case '6': { - // najbliższy z mipmapy - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); - break; - } - case '7': { - //średnia z mipmapy - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); - break; - } - case '8': { - // najbliższy z dwóch mipmap - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR ); - break; - } - case '9': { - //średnia z dwóch mipmap - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - break; - } -*/ + default: { break; } } } @@ -718,9 +682,27 @@ opengl_texture::downsize( GLuint const Format ) { }; } +void +texture_manager::assign_units( GLint const Helper, GLint const Shadows, GLint const Normals, GLint const Diffuse ) { + + m_units[ 0 ].unit = Helper; + m_units[ 1 ].unit = Shadows; + m_units[ 2 ].unit = Normals; + m_units[ 3 ].unit = Diffuse; +} + +void +texture_manager::unit( GLint const Textureunit ) { + + if( m_activeunit == Textureunit ) { return; } + + m_activeunit = Textureunit; + ::glActiveTexture( Textureunit ); +} + // ustalenie numeru tekstury, wczytanie jeśli jeszcze takiej nie było texture_handle -texture_manager::create( std::string Filename, std::string const &Dir, int const Filter, bool const Loadnow ) { +texture_manager::create( std::string Filename, bool const Loadnow ) { if( Filename.find( '|' ) != std::string::npos ) Filename.erase( Filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury @@ -801,11 +783,7 @@ texture_manager::create( std::string Filename, std::string const &Dir, int const auto texture = new opengl_texture(); texture->name = filename; - if( ( Filter > 0 ) && ( Filter < 10 ) ) { - // temporary. TODO, TBD: check how it's used and possibly get rid of it - traits += std::to_string( ( Filter < 4 ? Filter + 4 : Filter ) ); - } - if( Filename.find('#') !=std::string::npos ) { + if( Filename.find('#') != std::string::npos ) { // temporary code for legacy assets -- textures with names beginning with # are to be sharpened traits += '#'; } @@ -830,33 +808,36 @@ texture_manager::create( std::string Filename, std::string const &Dir, int const }; void -texture_manager::bind( texture_handle const Texture ) { +texture_manager::bind( std::size_t const Unit, texture_handle const Texture ) { m_textures[ Texture ].second = m_garbagecollector.timestamp(); - if( ( Texture != 0 ) && ( Texture == m_activetexture ) ) { + if( Texture == m_units[ Unit ].texture ) { // don't bind again what's already active return; } - // TODO: do binding in texture object, add support for other types - if( Texture != 0 ) { + // TBD, TODO: do binding in texture object, add support for other types than 2d + if( m_units[ Unit ].unit == 0 ) { return; } + unit( m_units[ Unit ].unit ); + if( Texture != null_handle ) { #ifndef EU07_DEFERRED_TEXTURE_UPLOAD // NOTE: we could bind dedicated 'error' texture here if the id isn't valid ::glBindTexture( GL_TEXTURE_2D, texture(Texture).id ); - m_activetexture = Texture; + m_units[ Unit ].texture = Texture; #else - if( texture( Texture ).bind() == resource_state::good ) { - m_activetexture = Texture; + if( true == texture( Texture ).bind() ) { + m_units[ Unit ].texture = Texture; } else { // TODO: bind a special 'error' texture on failure + ::glBindTexture( GL_TEXTURE_2D, 0 ); + m_units[ Unit ].texture = 0; } #endif } else { - ::glBindTexture( GL_TEXTURE_2D, 0 ); - m_activetexture = 0; + m_units[ Unit ].texture = 0; } // all done return; @@ -879,7 +860,9 @@ void texture_manager::update() { if( m_garbagecollector.sweep() > 0 ) { - m_activetexture = -1; + for( auto &unit : m_units ) { + unit.texture = -1; + } } } @@ -943,21 +926,10 @@ texture_manager::find_in_databank( std::string const &Texturename ) const { std::string texture_manager::find_on_disk( std::string const &Texturename ) const { - { - std::ifstream file( Texturename ); - if( true == file.is_open() ) { - // success - return Texturename; - } - } - // if we fail make a last ditch attempt in the default textures directory - { - std::ifstream file( szTexturePath + Texturename ); - if( true == file.is_open() ) { - // success - return szTexturePath + Texturename; - } - } - // no results either way, report failure - return ""; + return( + FileExists( Texturename ) ? Texturename : + FileExists( szTexturePath + Texturename ) ? szTexturePath + Texturename : + "" ); } + +//--------------------------------------------------------------------------- diff --git a/Texture.h b/Texture.h index 69119271..62235f4a 100644 --- a/Texture.h +++ b/Texture.h @@ -31,7 +31,7 @@ struct opengl_texture { // methods void load(); - resource_state + bool bind(); bool create(); @@ -78,6 +78,7 @@ private: }; typedef int texture_handle; +int const null_handle = 0; class texture_manager { @@ -85,12 +86,17 @@ public: texture_manager(); ~texture_manager() { delete_textures(); } - texture_handle - create( std::string Filename, std::string const &Dir, int const Filter = -1, bool const Loadnow = true ); void - bind( texture_handle const Texture ); + assign_units( GLint const Helper, GLint const Shadows, GLint const Normals, GLint const Diffuse ); + void + unit( GLint const Textureunit ); + texture_handle + create( std::string Filename, bool const Loadnow = true ); + // binds specified texture to specified texture unit + void + bind( std::size_t const Unit, texture_handle const Texture ); opengl_texture & - texture( texture_handle const Texture ) { return *(m_textures[ Texture ].first); } + texture( texture_handle const Texture ) const { return *(m_textures[ Texture ].first); } // performs a resource sweep void update(); @@ -108,6 +114,11 @@ private: typedef std::unordered_map index_map; + struct texture_unit { + GLint unit { 0 }; + texture_handle texture { null_handle }; // current (most recently bound) texture + }; + // methods: // checks whether specified texture is in the texture bank. returns texture id, or npos. texture_handle @@ -123,7 +134,8 @@ private: texturetimepointpair_sequence m_textures; index_map m_texturemappings; garbage_collector m_garbagecollector { m_textures, 600, 60, "texture" }; - texture_handle m_activetexture { 0 }; // last i.e. currently bound texture + std::array m_units; + GLint m_activeunit { 0 }; }; // reduces provided data image to half of original size, using basic 2x2 average @@ -182,3 +194,5 @@ downsample( std::size_t const Width, std::size_t const Height, char *Imagedata ) } } } + +//--------------------------------------------------------------------------- diff --git a/Track.cpp b/Track.cpp index 8b55c4ca..bee712aa 100644 --- a/Track.cpp +++ b/Track.cpp @@ -483,20 +483,20 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) { parser->getTokens(); *parser >> str; // railtex - TextureID1 = ( + m_material1 = ( str == "none" ? - NULL : - GfxRenderer.Fetch_Texture( str ) ); + null_handle : + GfxRenderer.Fetch_Material( str ) ); parser->getTokens(); *parser >> fTexLength; // tex tile length if (fTexLength < 0.01) fTexLength = 4; // Ra: zabezpiecznie przed zawieszeniem parser->getTokens(); *parser >> str; // sub || railtex - TextureID2 = ( + m_material2 = ( str == "none" ? - NULL : - GfxRenderer.Fetch_Texture( str ) ); + null_handle : + GfxRenderer.Fetch_Material( str ) ); parser->getTokens(3); *parser >> fTexHeight1 >> fTexWidth >> fTexSlope; if (iCategoryFlag & 4) @@ -527,7 +527,7 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) // na przechyłce doliczyć jeszcze pół przechyłki } if( fRadius != 0 ) // gdy podany promień - segsize = clamp( 0.2 + std::fabs( fRadius ) * 0.02, 2.0, 10.0 ); + segsize = clamp( std::fabs( fRadius ) * ( 0.02 / Global::SplineFidelity ), 2.0 / Global::SplineFidelity, 10.0 ); else segsize = 10.0; // for straights, 10m per segment works good enough @@ -547,15 +547,15 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) SwitchExtension->Segments[0]->Init(p1, p2, segsize); // kopia oryginalnego toru } else if (iCategoryFlag & 2) - if (TextureID1 && fTexLength) + if (m_material1 && fTexLength) { // dla drogi trzeba ustalić proporcje boków nawierzchni float w, h; - GfxRenderer.Bind(TextureID1); + GfxRenderer.Bind_Material(m_material1); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (h != 0.0) fTexRatio1 = w / h; // proporcja boków - GfxRenderer.Bind(TextureID2); + GfxRenderer.Bind_Material(m_material2); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (h != 0.0) @@ -762,12 +762,6 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) iAction |= 0x40; // flaga opuszczenia pantografu (tor uwzględniany w skanowaniu jako // ograniczenie dla pantografujących) } - else if (str == "colides") - { // informacja o stanie sieci: 0-jazda bezprądowa, >0-z opuszczonym i ograniczeniem prędkości - parser->getTokens(); - *parser >> token; - // trColides=; //tor kolizyjny, na którym trzeba sprawdzać pojazdy pod kątem zderzenia - } else ErrorLog("Unknown property: \"" + str + "\" in track \"" + name + "\""); parser->getTokens(); @@ -784,145 +778,127 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) } } +// TODO: refactor this mess bool TTrack::AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2) { bool bError = false; - if (!evEvent0) - { - if (NewEvent0) - { + + if( NewEvent0 == nullptr ) { + if( false == asEvent0Name.empty() ) { + ErrorLog( "Bad event: event \"" + asEvent0Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + bError = true; + } + } + else { + if( evEvent0 == nullptr ) { evEvent0 = NewEvent0; asEvent0Name = ""; iEvents |= 1; // sumaryczna informacja o eventach } - else - { - if (!asEvent0Name.empty()) - { - ErrorLog("Bad track: Event0 \"" + asEvent0Name + - "\" does not exist"); - bError = true; - } + else { + ErrorLog( "Bad track: event \"" + NewEvent0->asName + "\" cannot be assigned to track, track already has one" ); + bError = true; } } - else - { - ErrorLog( "Bad track: Event0 cannot be assigned to track, track already has one"); - bError = true; + + if( NewEvent1 == nullptr ) { + if( false == asEvent1Name.empty() ) { + ErrorLog( "Bad event: event \"" + asEvent1Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + bError = true; + } } - if (!evEvent1) - { - if (NewEvent1) - { + else { + if( evEvent1 == nullptr ) { evEvent1 = NewEvent1; asEvent1Name = ""; iEvents |= 2; // sumaryczna informacja o eventach } - else if (!asEvent1Name.empty()) - { // Ra: tylko w logu informacja - ErrorLog("Bad track: Event1 \"" + asEvent1Name + "\" does not exist"); + else { + ErrorLog( "Bad track: event \"" + NewEvent1->asName + "\" cannot be assigned to track, track already has one" ); bError = true; } } - else - { - ErrorLog("Bad track: Event1 cannot be assigned to track, track already has one"); - bError = true; + + if( NewEvent2 == nullptr ) { + if( false == asEvent2Name.empty() ) { + ErrorLog( "Bad event: event \"" + asEvent2Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + bError = true; + } } - if (!evEvent2) - { - if (NewEvent2) - { + else { + if( evEvent2 == nullptr ) { evEvent2 = NewEvent2; asEvent2Name = ""; iEvents |= 4; // sumaryczna informacja o eventach } - else if (!asEvent2Name.empty()) - { // Ra: tylko w logu informacja - ErrorLog("Bad track: Event2 \"" + asEvent2Name + "\" does not exist"); + else { + ErrorLog( "Bad track: event \"" + NewEvent2->asName + "\" cannot be assigned to track, track already has one" ); bError = true; } } - else - { - ErrorLog("Bad track: Event2 cannot be assigned to track, track already has one"); - bError = true; - } - return !bError; + + return ( bError == false ); } bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2) { bool bError = false; - if (!evEventall0) - { - if (NewEvent0) - { + + if( NewEvent0 == nullptr ) { + if( false == asEventall0Name.empty() ) { + ErrorLog( "Bad event: event \"" + asEventall0Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + bError = true; + } + } + else { + if( evEventall0 == nullptr ) { evEventall0 = NewEvent0; asEventall0Name = ""; iEvents |= 8; // sumaryczna informacja o eventach } - else - { - if (!asEvent0Name.empty()) - { - Error("Eventall0 \"" + asEventall0Name + - "\" does not exist"); - bError = true; - } + else { + ErrorLog( "Bad track: event \"" + NewEvent0->asName + "\" cannot be assigned to track, track already has one" ); + bError = true; } } - else - { - Error("Eventall0 cannot be assigned to track, track already has one"); - bError = true; + + if( NewEvent1 == nullptr ) { + if( false == asEventall1Name.empty() ) { + ErrorLog( "Bad event: event \"" + asEventall1Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + bError = true; + } } - if (!evEventall1) - { - if (NewEvent1) - { - evEventall1 = NewEvent1; + else { + if( evEventall1 == nullptr ) { + evEventall1 = NewEvent0; asEventall1Name = ""; iEvents |= 16; // sumaryczna informacja o eventach } - else - { - if (!asEvent0Name.empty()) - { // Ra: tylko w logu informacja - WriteLog("Eventall1 \"" + asEventall1Name + "\" does not exist"); - bError = true; - } + else { + ErrorLog( "Bad track: event \"" + NewEvent1->asName + "\" cannot be assigned to track, track already has one" ); + bError = true; } } - else - { - Error("Eventall1 cannot be assigned to track, track already has one"); - bError = true; + + if( NewEvent2 == nullptr ) { + if( false == asEventall2Name.empty() ) { + ErrorLog( "Bad event: event \"" + asEventall2Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + bError = true; + } } - if (!evEventall2) - { - if (NewEvent2) - { - evEventall2 = NewEvent2; + else { + if( evEventall2 == nullptr ) { + evEventall2 = NewEvent0; asEventall2Name = ""; iEvents |= 32; // sumaryczna informacja o eventach } - else - { - if (!asEvent0Name.empty()) - { // Ra: tylko w logu informacja - WriteLog("Eventall2 \"" + asEventall2Name + - "\" does not exist"); - bError = true; - } + else { + ErrorLog( "Bad track: event \"" + NewEvent2->asName + "\" cannot be assigned to track, track already has one" ); + bError = true; } } - else - { - Error("Eventall2 cannot be assigned to track, track already has one"); - bError = true; - } - return !bError; + + return ( bError == false ); } bool TTrack::AssignForcedEvents(TEvent *NewEventPlus, TEvent *NewEventMinus) @@ -1085,8 +1061,10 @@ bool TTrack::InMovement() if (!SwitchExtension->CurrentIndex) return false; // 0=zablokowana się nie animuje // trzeba każdorazowo porównywać z kątem modelu - TAnimContainer *ac = - SwitchExtension->pModel ? SwitchExtension->pModel->GetContainer(NULL) : NULL; + TAnimContainer *ac = ( + SwitchExtension->pModel ? + SwitchExtension->pModel->GetContainer() : + nullptr ); return ac ? (ac->AngleGet() != SwitchExtension->fOffset) || !(ac->TransGet() == SwitchExtension->vTrans) : @@ -1096,10 +1074,7 @@ bool TTrack::InMovement() } return false; }; -void TTrack::RaAssign(TGroundNode *gn, TAnimContainer *ac){ - // Ra: wiązanie toru z modelem obrotnicy - // if (eType==tt_Table) SwitchExtension->pAnim=p; -}; + void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined) { // Ra: wiązanie toru z modelem obrotnicy if (eType == tt_Table) @@ -1110,8 +1085,8 @@ void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joi SwitchExtension->evPlus = joined; // event potwierdzenia połączenia (gdy nie znajdzie, to się nie połączy) if (am) - if (am->GetContainer(NULL)) // może nie być? - am->GetContainer(NULL)->EventAssign(done); // zdarzenie zakończenia animacji + if (am->GetContainer()) // może nie być? + am->GetContainer()->EventAssign(done); // zdarzenie zakończenia animacji } }; @@ -1205,7 +1180,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { case tt_Table: // obrotnica jak zwykły tor, tylko animacja dochodzi case tt_Normal: - if (TextureID2) + if (m_material2) { // podsypka z podkładami jest tylko dla zwykłego toru vector6 bpts1[8]; // punkty głównej płaszczyzny nie przydają się do robienia boków if (fTexLength == 4.0) // jeśli stare mapowanie @@ -1275,7 +1250,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { GfxRenderer.Replace( vertices, Geometry2[ 0 ] ); } } - if (TextureID1) + if (m_material1) { // szyny - generujemy dwie, najwyżej rysować się będzie jedną vertex_array vertices; if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) { @@ -1296,7 +1271,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { } break; case tt_Switch: // dla zwrotnicy dwa razy szyny - if( TextureID1 || TextureID2 ) + if( m_material1 || m_material2 ) { // iglice liczone tylko dla zwrotnic vector6 rpts3[24], rpts4[24]; for (i = 0; i < 12; ++i) @@ -1326,7 +1301,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa vertex_array vertices; - if( TextureID1 ) { + if( m_material1 ) { // fixed parts SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1339,7 +1314,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // fixed parts SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1356,7 +1331,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { else { // lewa działa lepiej niż prawa vertex_array vertices; - if( TextureID1 ) { + if( m_material1 ) { // fixed parts SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength ); // lewa szyna normalna cała Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1369,7 +1344,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // fixed parts SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength ); // prawa szyna normalnie cała Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1394,7 +1369,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { case tt_Normal: // drogi proste, bo skrzyżowania osobno { vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (TextureID1 || TextureID2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma + if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% double max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 @@ -1413,13 +1388,13 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { bpts1[ 1 ] = vector6( -fHTW, 0.0, 0.5 + map1, 0.0, 1.0, 0.0 ); } } - if (TextureID1) // jeśli podana była tekstura, generujemy trójkąty + if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy vertex_array vertices; Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } - if (TextureID2) + if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vector6 rpts1[6], @@ -1574,7 +1549,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { nullptr : SwitchExtension->vPoints; // zmienna robocza, NULL gdy tablica punktów już jest wypełniona vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (TextureID1 || TextureID2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma + if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% double max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 @@ -1592,7 +1567,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { // problem ze skrzyżowaniami jest taki, że teren chce się pogrupować wg tekstur, ale zaczyna od nawierzchni // sama nawierzchnia nie wypełni tablicy punktów, bo potrzebne są pobocza // ale pobocza renderują się później, więc nawierzchnia nie załapuje się na renderowanie w swoim czasie - if( TextureID2 ) + if( m_material2 ) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vector6 rpts1[6], @@ -1651,7 +1626,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { rpts2[5] = vector6(bpts1[3].x - side2, bpts1[3].y - fTexHeight2, 0.484375 - map2l); // lewy brzeg lewego chodnika } } - bool render = ( TextureID2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni + bool render = ( m_material2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni vertex_array vertices; if (SwitchExtension->iRoads == 4) { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka @@ -1707,7 +1682,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { SwitchExtension->bPoints = true; // tablica punktów została wypełniona } - if( TextureID1 ) { + if( m_material1 ) { vertex_array vertices; // jeśli podana tekstura nawierzchni // we start with a vertex in the middle... @@ -1759,7 +1734,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { case tt_Normal: // drogi proste, bo skrzyżowania osobno { vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (TextureID1 || TextureID2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma + if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% double max = (iCategoryFlag & 4) ? 0.0 : @@ -1784,13 +1759,13 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { bpts1[1] = vector6(-fHTW, 0.0, 0.5 + map1); } } - if (TextureID1) // jeśli podana była tekstura, generujemy trójkąty + if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy vertex_array vertices; Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } - if (TextureID2) + if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vertex_array vertices; vector6 rpts1[6], @@ -2105,8 +2080,8 @@ TTrack * TTrack::RaAnimate() } } // skip the geometry update if no geometry for this track was generated yet - if( ( ( TextureID1 != 0 ) - || ( TextureID2 != 0 ) ) + if( ( ( m_material1 != 0 ) + || ( m_material2 != 0 ) ) && ( ( false == Geometry1.empty() ) || ( false == Geometry2.empty() ) ) ) { // iglice liczone tylko dla zwrotnic @@ -2143,13 +2118,13 @@ TTrack * TTrack::RaAnimate() if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa - if( TextureID1 ) { + if( m_material1 ) { // left blade SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); GfxRenderer.Replace( vertices, Geometry1[ 2 ] ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // right blade SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 ); GfxRenderer.Replace( vertices, Geometry2[ 2 ] ); @@ -2157,13 +2132,13 @@ TTrack * TTrack::RaAnimate() } } else { // lewa działa lepiej niż prawa - if( TextureID1 ) { + if( m_material1 ) { // right blade SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); GfxRenderer.Replace( vertices, Geometry1[ 2 ] ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // left blade SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 ); GfxRenderer.Replace( vertices, Geometry2[ 2 ] ); @@ -2178,9 +2153,10 @@ TTrack * TTrack::RaAnimate() SwitchExtension->CurrentIndex) // 0=zablokowana się nie animuje { // trzeba każdorazowo porównywać z kątem modelu // //pobranie kąta z modelu - TAnimContainer *ac = SwitchExtension->pModel ? - SwitchExtension->pModel->GetContainer(NULL) : - NULL; // pobranie głównego submodelu + TAnimContainer *ac = ( + SwitchExtension->pModel ? + SwitchExtension->pModel->GetContainer() : // pobranie głównego submodelu + nullptr ); if (ac) if ((ac->AngleGet() != SwitchExtension->fOffset) || !(ac->TransGet() == @@ -2319,7 +2295,7 @@ int TTrack::TestPoint(vector3 *Point) return -1; }; -void TTrack::MovedUp1(double dh) +void TTrack::MovedUp1(float const dh) { // poprawienie przechyłki wymaga wydłużenia podsypki fTexHeight1 += dh; }; diff --git a/Track.h b/Track.h index 884d6907..af635859 100644 --- a/Track.h +++ b/Track.h @@ -13,7 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "ResourceManager.h" #include "Segment.h" -#include "Texture.h" +#include "material.h" typedef enum { @@ -139,11 +139,9 @@ private: float fTexHeight1 = 0.6f; // wysokość brzegu względem trajektorii float fTexWidth = 0.9f; // szerokość boku float fTexSlope = 0.9f; -/* - GLuint DisplayListID = 0; -*/ - texture_handle TextureID1 = 0; // tekstura szyn albo nawierzchni - texture_handle TextureID2 = 0; // tekstura automatycznej podsypki albo pobocza + + material_handle m_material1 = 0; // tekstura szyn albo nawierzchni + material_handle m_material2 = 0; // tekstura automatycznej podsypki albo pobocza typedef std::vector geometryhandle_sequence; geometryhandle_sequence Geometry1; // geometry chunks textured with texture 1 geometryhandle_sequence Geometry2; // geometry chunks textured with texture 2 @@ -187,7 +185,6 @@ public: bool ScannedFlag = false; // McZapkie: do zaznaczania kolorem torów skanowanych przez AI TTraction *hvOverhead = nullptr; // drut zasilający do szybkiego znalezienia (nie używany) TGroundNode *nFouling[ 2 ]; // współrzędne ukresu albo oporu kozła - TTrack *trColides = nullptr; // tor kolizyjny, na którym trzeba sprawdzać pojazdy pod kątem zderzenia TTrack(TGroundNode *g); ~TTrack(); @@ -225,24 +222,14 @@ public: bool CheckDynamicObject(TDynamicObject *Dynamic); bool AddDynamicObject(TDynamicObject *Dynamic); bool RemoveDynamicObject(TDynamicObject *Dynamic); -/* - void Release(); - void Compile(GLuint tex = 0); - void Render(); // renderowanie z Display Lists - int RaArrayPrepare(); // zliczanie rozmiaru dla VBO sektroa -*/ void create_geometry(geometrybank_handle const &Bank); // wypełnianie VBO -/* - void RaRenderVBO(int iPtr); // renderowanie z VBO sektora -*/ void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania void RaOwnerSet(TSubRect *o) { if (SwitchExtension) SwitchExtension->pOwner = o; }; bool InMovement(); // czy w trakcie animacji? - void RaAssign(TGroundNode *gn, TAnimContainer *ac); void RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined); void RaAnimListAdd(TTrack *t); TTrack * RaAnimate(); @@ -257,11 +244,11 @@ public: GLuint TextureGet(int i) { return ( i ? - TextureID1 : - TextureID2 ); }; + m_material1 : + m_material2 ); }; bool IsGroupable(); int TestPoint(vector3 *Point); - void MovedUp1(double dh); + void MovedUp1(float const dh); std::string NameGet(); void VelocitySet(float v); float VelocityGet(); diff --git a/Traction.cpp b/Traction.cpp index 0b6028fb..4f1ab726 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -93,7 +93,7 @@ sekcji z sąsiedniego przęsła). std::size_t TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { - if( m_geometry != NULL ) { + if( m_geometry != null_handle ) { return GfxRenderer.Vertices( m_geometry ).size() / 2; } diff --git a/Train.cpp b/Train.cpp index 3db1a780..c6f54547 100644 --- a/Train.cpp +++ b/Train.cpp @@ -217,6 +217,9 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::trainbrakeservice, &TTrain::OnCommand_trainbrakeservice }, { user_command::trainbrakefullservice, &TTrain::OnCommand_trainbrakefullservice }, { user_command::trainbrakeemergency, &TTrain::OnCommand_trainbrakeemergency }, + { user_command::manualbrakeincrease, &TTrain::OnCommand_manualbrakeincrease }, + { user_command::manualbrakedecrease, &TTrain::OnCommand_manualbrakedecrease }, + { user_command::alarmchaintoggle, &TTrain::OnCommand_alarmchaintoggle }, { user_command::wheelspinbrakeactivate, &TTrain::OnCommand_wheelspinbrakeactivate }, { user_command::sandboxactivate, &TTrain::OnCommand_sandboxactivate }, { user_command::epbrakecontroltoggle, &TTrain::OnCommand_epbrakecontroltoggle }, @@ -264,6 +267,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::hornlowactivate, &TTrain::OnCommand_hornlowactivate }, { user_command::hornhighactivate, &TTrain::OnCommand_hornhighactivate }, { user_command::radiotoggle, &TTrain::OnCommand_radiotoggle }, + { user_command::radiostoptest, &TTrain::OnCommand_radiostoptest }, { user_command::generictoggle0, &TTrain::OnCommand_generictoggle }, { user_command::generictoggle1, &TTrain::OnCommand_generictoggle }, { user_command::generictoggle2, &TTrain::OnCommand_generictoggle }, @@ -557,14 +561,7 @@ void TTrain::OnCommand_mastercontrollerincrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvControlled->IncMainCtrl( 1 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikJazdy ); - } -#else Train->mvControlled->IncMainCtrl( 1 ); -#endif } } @@ -572,14 +569,7 @@ void TTrain::OnCommand_mastercontrollerincreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvControlled->IncMainCtrl( 2 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikJazdy ); - } -#else Train->mvControlled->IncMainCtrl( 2 ); -#endif } } @@ -587,14 +577,7 @@ void TTrain::OnCommand_mastercontrollerdecrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvControlled->DecMainCtrl( 1 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikJazdy ); - } -#else Train->mvControlled->DecMainCtrl( 1 ); -#endif } } @@ -602,14 +585,7 @@ void TTrain::OnCommand_mastercontrollerdecreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvControlled->DecMainCtrl( 2 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikJazdy ); - } -#else Train->mvControlled->DecMainCtrl( 2 ); -#endif } } @@ -622,16 +598,9 @@ void TTrain::OnCommand_secondcontrollerincrease( TTrain *Train, command_data con if( Train->mvControlled->AnPos > 1 ) Train->mvControlled->AnPos = 1; } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - else if( Train->mvControlled->IncScndCtrl( 1 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikBocz, Train->dsbNastawnikJazdy, DSBVOLUME_MAX, 0 ); - } -#else else { Train->mvControlled->IncScndCtrl( 1 ); } -#endif } } @@ -639,14 +608,7 @@ void TTrain::OnCommand_secondcontrollerincreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvControlled->IncScndCtrl( 2 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikBocz, Train->dsbNastawnikJazdy, DSBVOLUME_MAX, 0 ); - } -#else Train->mvControlled->IncScndCtrl( 2 ); -#endif } } @@ -656,31 +618,11 @@ void TTrain::OnCommand_notchingrelaytoggle( TTrain *Train, command_data const &C // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvOccupied->AutoRelayFlag ) { // turn on -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvOccupied->AutoRelaySwitch( true ) ) { - // audio feedback - Train->play_sound( Train->dsbSwitch ); - // visual feedback - // NOTE: there's no button for notching relay control - // TBD, TODO: add notching relay control button? - } -#else Train->mvOccupied->AutoRelaySwitch( true ); -#endif } else { //turn off -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvOccupied->AutoRelaySwitch( false ) ) { - // audio feedback - Train->play_sound( Train->dsbSwitch ); - // visual feedback - // NOTE: there's no button for notching relay control - // TBD, TODO: add notching relay control button? - } -#else Train->mvOccupied->AutoRelaySwitch( false ); -#endif } } } @@ -697,24 +639,12 @@ void TTrain::OnCommand_mucurrentindicatorothersourceactivate( TTrain *Train, com if( Command.action == GLFW_PRESS ) { // turn on Train->ShowNextCurrent = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggNextCurrentButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggNextCurrentButton.UpdateValue( 1.0, Train->dsbSwitch ); } else if( Command.action == GLFW_RELEASE ) { //turn off Train->ShowNextCurrent = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggNextCurrentButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggNextCurrentButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -729,14 +659,7 @@ void TTrain::OnCommand_secondcontrollerdecrease( TTrain *Train, command_data con if( Train->mvControlled->AnPos > 1 ) Train->mvControlled->AnPos = 1; } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - else if( Train->mvControlled->DecScndCtrl( 1 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikBocz, Train->dsbNastawnikJazdy, DSBVOLUME_MAX, 0 ); - } -#else Train->mvControlled->DecScndCtrl( 1 ); -#endif } } @@ -744,14 +667,7 @@ void TTrain::OnCommand_secondcontrollerdecreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( Train->mvControlled->DecScndCtrl( 2 ) ) { - // sound feedback - Train->play_sound( Train->dsbNastawnikBocz, Train->dsbNastawnikJazdy, DSBVOLUME_MAX, 0 ); - } -#else Train->mvControlled->DecScndCtrl( 2 ); -#endif } } @@ -813,12 +729,6 @@ void TTrain::OnCommand_independentbrakebailoff( TTrain *Train, command_data cons if( Command.action != GLFW_RELEASE ) { // press or hold Train->mvOccupied->BrakeReleaser( 1 ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggReleaserButton.GetValue() < 0.05 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggReleaserButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -929,7 +839,7 @@ void TTrain::OnCommand_trainbrakecharging( TTrain *Train, command_data const &Co void TTrain::OnCommand_trainbrakerelease( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_RELEASE ) { + if( Command.action == GLFW_PRESS ) { // sound feedback if( ( Train->is_eztoer() ) @@ -944,7 +854,7 @@ void TTrain::OnCommand_trainbrakerelease( TTrain *Train, command_data const &Com void TTrain::OnCommand_trainbrakefirstservice( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_RELEASE ) { + if( Command.action == GLFW_PRESS ) { // sound feedback if( ( Train->is_eztoer() ) @@ -959,7 +869,7 @@ void TTrain::OnCommand_trainbrakefirstservice( TTrain *Train, command_data const void TTrain::OnCommand_trainbrakeservice( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_RELEASE ) { + if( Command.action == GLFW_PRESS ) { // sound feedback if( ( Train->is_eztoer() ) @@ -972,14 +882,14 @@ void TTrain::OnCommand_trainbrakeservice( TTrain *Train, command_data const &Com Train->mvOccupied->BrakeLevelSet( Train->mvOccupied->BrakeCtrlPosNo / 2 + ( Train->mvOccupied->BrakeHandle == FV4a ? - 1 : - 0 ) ); + 1 : + 0 ) ); } } void TTrain::OnCommand_trainbrakefullservice( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_RELEASE ) { + if( Command.action == GLFW_PRESS ) { // sound feedback if( ( Train->is_eztoer() ) @@ -994,12 +904,57 @@ void TTrain::OnCommand_trainbrakefullservice( TTrain *Train, command_data const void TTrain::OnCommand_trainbrakeemergency( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_RELEASE ) { + if( Command.action == GLFW_PRESS ) { Train->mvOccupied->BrakeLevelSet( Train->mvOccupied->Handle->GetPos( bh_EB ) ); +/* if( Train->mvOccupied->BrakeCtrlPosNo <= 0.1 ) { // hamulec bezpieczeństwa dla wagonów - Train->mvOccupied->EmergencyBrakeFlag = true; + Train->mvOccupied->RadioStopFlag = true; + } +*/ + } +} + +void TTrain::OnCommand_manualbrakeincrease( TTrain *Train, command_data const &Command ) { + + if( Command.action != GLFW_RELEASE ) { + + if( ( Train->mvOccupied->LocalBrake == ManualBrake ) + || ( Train->mvOccupied->MBrake == true ) ) { + + Train->mvOccupied->IncManualBrakeLevel( 1 ); + } + } +} + +void TTrain::OnCommand_manualbrakedecrease( TTrain *Train, command_data const &Command ) { + + if( Command.action != GLFW_RELEASE ) { + + if( ( Train->mvOccupied->LocalBrake == ManualBrake ) + || ( Train->mvOccupied->MBrake == true ) ) { + + Train->mvOccupied->DecManualBrakeLevel( 1 ); + } + } +} + +void TTrain::OnCommand_alarmchaintoggle( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + + if( false == Train->mvOccupied->AlarmChainFlag ) { + // pull + Train->mvOccupied->AlarmChainSwitch( true ); + // visual feedback + Train->ggAlarmChain.UpdateValue( 1.0 ); + } + else { + // release + Train->mvOccupied->AlarmChainSwitch( false ); + // visual feedback + Train->ggAlarmChain.UpdateValue( 0.0 ); } } } @@ -1020,12 +975,6 @@ void TTrain::OnCommand_wheelspinbrakeactivate( TTrain *Train, command_data const if( Command.action != GLFW_RELEASE ) { // press or hold Train->mvControlled->AntiSlippingBrake(); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggAntiSlipButton.GetValue() < 0.05 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggAntiSlipButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -1055,10 +1004,6 @@ void TTrain::OnCommand_sandboxactivate( TTrain *Train, command_data const &Comma if( Command.action == GLFW_PRESS ) { // press Train->mvControlled->Sandbox( true ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -#endif // visual feedback Train->ggSandButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -1115,13 +1060,6 @@ void TTrain::OnCommand_brakeactingspeedincrease( TTrain *Train, command_data con Train->mvOccupied->BrakeDelayFlag << 1 : Train->mvOccupied->BrakeDelayFlag | bdelay_M ); if( true == Train->mvOccupied->BrakeDelaySwitch( fasterbrakesetting ) ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -/* - Train->play_sound( Train->dsbPneumaticRelay ); -*/ -#endif // visual feedback if( Train->ggBrakeProfileCtrl.SubModel != nullptr ) { Train->ggBrakeProfileCtrl.UpdateValue( @@ -1160,13 +1098,6 @@ void TTrain::OnCommand_brakeactingspeeddecrease( TTrain *Train, command_data con Train->mvOccupied->BrakeDelayFlag >> 1 : Train->mvOccupied->BrakeDelayFlag ^ bdelay_M ); if( true == Train->mvOccupied->BrakeDelaySwitch( slowerbrakesetting ) ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -/* - Train->play_sound( Train->dsbPneumaticRelay ); -*/ -#endif // visual feedback if( Train->ggBrakeProfileCtrl.SubModel != nullptr ) { Train->ggBrakeProfileCtrl.UpdateValue( @@ -1211,24 +1142,12 @@ void TTrain::OnCommand_mubrakingindicatortoggle( TTrain *Train, command_data con if( false == Train->mvControlled->Signalling) { // turn on Train->mvControlled->Signalling = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggSignallingButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggSignallingButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { //turn off Train->mvControlled->Signalling = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggSignallingButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggSignallingButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -1240,10 +1159,6 @@ void TTrain::OnCommand_reverserincrease( TTrain *Train, command_data const &Comm if( Command.action == GLFW_PRESS ) { if( Train->mvOccupied->DirectionForward() ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbReverserKey, Train->dsbSwitch, DSBVOLUME_MAX, 0 ); -#endif // aktualizacja skrajnych pojazdów w składzie if( ( Train->mvOccupied->ActiveDir ) && ( Train->DynamicObject->Mechanik ) ) { @@ -1259,10 +1174,6 @@ void TTrain::OnCommand_reverserdecrease( TTrain *Train, command_data const &Comm if( Command.action == GLFW_PRESS ) { if( Train->mvOccupied->DirectionBackward() ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbReverserKey, Train->dsbSwitch, DSBVOLUME_MAX, 0 ); -#endif // aktualizacja skrajnych pojazdów w składzie if( ( Train->mvOccupied->ActiveDir ) && ( Train->DynamicObject->Mechanik ) ) { @@ -1289,12 +1200,6 @@ void TTrain::OnCommand_alerteracknowledge( TTrain *Train, command_data const &Co } // visual feedback Train->ggSecurityResetButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggSecurityResetButton.GetValue() < 0.05 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { // release @@ -1322,10 +1227,6 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command if( Train->ggBatteryButton.SubModel ) { Train->ggBatteryButton.UpdateValue( 1.0, Train->dsbSwitch ); } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -#endif // side-effects if( Train->mvOccupied->LightsPosNo > 0 ) { Train->SetLights(); @@ -1344,10 +1245,6 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command if( Train->ggBatteryButton.SubModel ) { Train->ggBatteryButton.UpdateValue( 0.0, Train->dsbSwitch ); } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -#endif // side-effects if( false == Train->mvControlled->ConverterFlag ) { // if there's no (low voltage) power source left, drop pantographs @@ -1533,18 +1430,10 @@ void TTrain::OnCommand_pantographcompressorvalvetoggle( TTrain *Train, command_d if( Train->mvControlled->bPantKurek3 == false ) { // connect pantographs with primary tank Train->mvControlled->bPantKurek3 = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -#endif } else { // connect pantograps with pantograph compressor Train->mvControlled->bPantKurek3 = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - Train->play_sound( Train->dsbSwitch ); -#endif } } } @@ -1567,12 +1456,6 @@ void TTrain::OnCommand_pantographcompressoractivate( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // press or hold to activate Train->mvControlled->PantCompFlag = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Command.action == GLFW_PRESS ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { // release to disable @@ -1599,13 +1482,6 @@ void TTrain::OnCommand_pantographlowerall( TTrain *Train, command_data const &Co // ...and rear Train->mvControlled->PantRearSP = false; Train->mvControlled->PantRear( false ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - // TODO: separate sound effect for pneumatic buttons - if( Train->ggPantAllDownButton.GetValue() < 0.35 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback if( Train->ggPantAllDownButton.SubModel ) Train->ggPantAllDownButton.UpdateValue( 1.0, Train->dsbSwitch ); @@ -1615,16 +1491,6 @@ void TTrain::OnCommand_pantographlowerall( TTrain *Train, command_data const &Co } else if( Command.action == GLFW_RELEASE ) { // release the button -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback -/* - // NOTE: release sound disabled as this is typically pneumatic button - // TODO: separate sound effect for pneumatic buttons - if( Train->ggPantAllDownButton.GetValue() > 0.65 ) { - Train->play_sound( Train->dsbSwitch ); - } -*/ -#endif // visual feedback if( Train->ggPantAllDownButton.SubModel ) Train->ggPantAllDownButton.UpdateValue( 0.0 ); @@ -1663,23 +1529,11 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com } if( Train->ggMainOnButton.SubModel != nullptr ) { // two separate switches to close and break the circuit -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Command.action == GLFW_PRESS ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggMainOnButton.UpdateValue( 1.0, Train->dsbSwitch ); } else if( Train->ggMainButton.SubModel != nullptr ) { // single two-state switch -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Command.action == GLFW_PRESS ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggMainButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -1722,12 +1576,6 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com if( Train->ggMainOffButton.SubModel != nullptr ) { // two separate switches to close and break the circuit -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Command.action == GLFW_PRESS ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggMainOffButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -1748,12 +1596,6 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com else */ { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Command.action == GLFW_PRESS ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggMainButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -1779,24 +1621,12 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com // for setup with two separate swiches if( Train->ggMainOnButton.SubModel != nullptr ) { Train->ggMainOnButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggMainOnButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } if( Train->ggMainOffButton.SubModel != nullptr ) { Train->ggMainOffButton.UpdateValue( 0.0, Train->dsbSwitch ); } // and the two-state switch too, for good measure if( Train->ggMainButton.SubModel != nullptr ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggMainButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggMainButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -1813,12 +1643,6 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com Train->mvControlled->CompressorSwitch( Train->ggCompressorButton.GetValue() > 0.5 ); } } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggMainOnButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback if( Train->ggMainOnButton.SubModel != nullptr ) { // setup with two separate switches @@ -1829,12 +1653,6 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com // TODO: have proper switch type config for all switches, and put it in the cab switch descriptions, not in the .fiz if( Train->mvControlled->TrainType == dt_EZT ) { if( Train->ggMainButton.SubModel != nullptr ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggMainButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggMainButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -1859,12 +1677,6 @@ void TTrain::OnCommand_convertertoggle( TTrain *Train, command_data const &Comma if( ( false == Train->mvControlled->ConverterAllow ) && ( Train->ggConverterButton.GetValue() < 0.5 ) ) { // turn on -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggConverterButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggConverterButton.UpdateValue( 1.0, Train->dsbSwitch ); /* @@ -1892,19 +1704,6 @@ void TTrain::OnCommand_convertertoggle( TTrain *Train, command_data const &Comma } else { //turn off -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->mvOccupied->ConvSwitchType == "impulse" ) { - if( Train->ggConverterOffButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } - } - else { - if( Train->ggConverterButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } - } -#endif // visual feedback Train->ggConverterButton.UpdateValue( 0.0, Train->dsbSwitch ); if( Train->ggConverterOffButton.SubModel != nullptr ) { @@ -1933,13 +1732,6 @@ void TTrain::OnCommand_convertertoggle( TTrain *Train, command_data const &Comma // on button release... if( Train->mvOccupied->ConvSwitchType == "impulse" ) { // ...return switches to start position if applicable -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( ( Train->ggConverterButton.GetValue() > 0.0 ) - || ( Train->ggConverterOffButton.GetValue() > 0.0 ) ) { - // sound feedback - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->ggConverterButton.UpdateValue( 0.0, Train->dsbSwitch ); Train->ggConverterOffButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -1961,12 +1753,6 @@ void TTrain::OnCommand_convertertogglelocal( TTrain *Train, command_data const & if( ( false == Train->mvOccupied->ConverterAllowLocal ) && ( Train->ggConverterLocalButton.GetValue() < 0.5 ) ) { // turn on -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggConverterLocalButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggConverterLocalButton.UpdateValue( 1.0, Train->dsbSwitch ); // effect @@ -1984,12 +1770,6 @@ void TTrain::OnCommand_convertertogglelocal( TTrain *Train, command_data const & } else { //turn off -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggConverterLocalButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggConverterLocalButton.UpdateValue( 0.0, Train->dsbSwitch ); // effect @@ -2029,23 +1809,11 @@ void TTrain::OnCommand_converteroverloadrelayreset( TTrain *Train, command_data && ( Train->mvControlled->TrainType != dt_EZT ) ) { Train->mvControlled->ConvOvldFlag = false; } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggConverterFuseButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggConverterFuseButton.UpdateValue( 1.0, Train->dsbSwitch ); } else if( Command.action == GLFW_RELEASE ) { // release -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggConverterFuseButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggConverterFuseButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2063,12 +1831,6 @@ void TTrain::OnCommand_compressortoggle( TTrain *Train, command_data const &Comm // turn on // visual feedback Train->ggCompressorButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggCompressorButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // impulse type switch has no effect if there's no power // NOTE: this is most likely setup wrong, but the whole thing is smoke and mirrors anyway // (we're presuming impulse type switch for all EMUs for the time being) @@ -2081,10 +1843,6 @@ void TTrain::OnCommand_compressortoggle( TTrain *Train, command_data const &Comm else { //turn off if( true == Train->mvControlled->CompressorSwitch( false ) ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - Train->play_sound( Train->dsbSwitch ); -#endif // NOTE: we don't have switch type definition for the compresor switch // so for the time being we have hard coded "impulse" switches for all EMUs // TODO: have proper switch type config for all switches, and put it in the cab switch descriptions, not in the .fiz @@ -2131,25 +1889,13 @@ void TTrain::OnCommand_compressortogglelocal( TTrain *Train, command_data const // only reacting to press, so the switch doesn't flip back and forth if key is held down if( false == Train->mvOccupied->CompressorAllowLocal ) { // turn on -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggCompressorLocalButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggCompressorLocalButton.UpdateValue( 1.0, Train->dsbSwitch ); // effect Train->mvOccupied->CompressorAllowLocal = true; } else { - //turn off -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggCompressorLocalButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif + // turn off // visual feedback Train->ggCompressorLocalButton.UpdateValue( 0.0, Train->dsbSwitch ); // effect @@ -2189,12 +1935,6 @@ void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &C Train->mvControlled->Couplers[ 1 ].Connected->StLinSwitchOff = true; } } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggStLinOffButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggStLinOffButton.UpdateValue( 1.0, Train->dsbSwitch ); // effect @@ -2238,12 +1978,6 @@ void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &C Train->mvControlled->Couplers[ 1 ].Connected->StLinSwitchOff = false; } } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggStLinOffButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggStLinOffButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2269,12 +2003,6 @@ void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &C Train->mvControlled->Couplers[ 1 ].Connected->StLinSwitchOff = false; } } -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggStLinOffButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggStLinOffButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2291,14 +2019,7 @@ void TTrain::OnCommand_motordisconnect( TTrain *Train, command_data const &Comma } if( Command.action == GLFW_PRESS ) { -#ifdef EU07_USE_OLD_CONTROLSOUNDS - if( true == Train->mvControlled->CutOffEngine() ) { - // sound feedback - Train->play_sound( Train->dsbSwitch ); - } -#else Train->mvControlled->CutOffEngine(); -#endif } } @@ -2311,12 +2032,6 @@ void TTrain::OnCommand_motoroverloadrelaythresholdtoggle( TTrain *Train, command if( true == Train->mvControlled->CurrentSwitch( true ) ) { // visual feedback Train->ggMaxCurrentCtrl.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggMaxCurrentCtrl.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } else { @@ -2324,12 +2039,6 @@ void TTrain::OnCommand_motoroverloadrelaythresholdtoggle( TTrain *Train, command if( true == Train->mvControlled->CurrentSwitch( false ) ) { // visual feedback Train->ggMaxCurrentCtrl.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggMaxCurrentCtrl.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2347,23 +2056,11 @@ void TTrain::OnCommand_motoroverloadrelayreset( TTrain *Train, command_data cons if( Command.action == GLFW_PRESS ) { // press Train->mvControlled->FuseOn(); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggFuseButton.GetValue() < 0.05 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggFuseButton.UpdateValue( 1.0, Train->dsbSwitch ); } else if( Command.action == GLFW_RELEASE ) { // release -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggFuseButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggFuseButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2388,24 +2085,16 @@ void TTrain::OnCommand_headlighttoggleleft( TTrain *Train, command_data const &C Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_left; // visual feedback Train->ggLeftLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggLeftLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); + // if the light is controlled by 3-way switch, disable marker light + if( Train->ggLeftEndLightButton.SubModel == nullptr ) { + Train->DynamicObject->iLights[ lightsindex ] &= ~TMoverParameters::light::redmarker_left; } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_left; // visual feedback Train->ggLeftLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggLeftLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2429,24 +2118,16 @@ void TTrain::OnCommand_headlighttoggleright( TTrain *Train, command_data const & Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_right; // visual feedback Train->ggRightLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRightLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); + // if the light is controlled by 3-way switch, disable marker light + if( Train->ggRightEndLightButton.SubModel == nullptr ) { + Train->DynamicObject->iLights[ lightsindex ] &= ~TMoverParameters::light::redmarker_right; } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_right; // visual feedback Train->ggRightLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRightLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2470,24 +2151,12 @@ void TTrain::OnCommand_headlighttoggleupper( TTrain *Train, command_data const & Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_upper; // visual feedback Train->ggUpperLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggUpperLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_upper; // visual feedback Train->ggUpperLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggUpperLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2510,25 +2179,29 @@ void TTrain::OnCommand_redmarkertoggleleft( TTrain *Train, command_data const &C // turn on Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_left; // visual feedback - Train->ggLeftEndLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggLeftEndLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); + if( Train->ggLeftEndLightButton.SubModel != nullptr ) { + Train->ggLeftEndLightButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch + // this is crude, but for now will do + Train->ggLeftLightButton.UpdateValue( -1.0, Train->dsbSwitch ); + // if the light is controlled by 3-way switch, disable the headlight + Train->DynamicObject->iLights[ lightsindex ] &= ~TMoverParameters::light::headlight_left; } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_left; // visual feedback - Train->ggLeftEndLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggLeftEndLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); + if( Train->ggLeftEndLightButton.SubModel != nullptr ) { + Train->ggLeftEndLightButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + else { + // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch + // this is crude, but for now will do + Train->ggLeftLightButton.UpdateValue( 0.0, Train->dsbSwitch ); } -#endif } } } @@ -2551,25 +2224,29 @@ void TTrain::OnCommand_redmarkertoggleright( TTrain *Train, command_data const & // turn on Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_right; // visual feedback - Train->ggRightEndLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRightEndLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); + if( Train->ggRightEndLightButton.SubModel != nullptr ) { + Train->ggRightEndLightButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch + // this is crude, but for now will do + Train->ggRightLightButton.UpdateValue( -1.0, Train->dsbSwitch ); + // if the light is controlled by 3-way switch, disable the headlight + Train->DynamicObject->iLights[ lightsindex ] &= ~TMoverParameters::light::headlight_right; } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_right; // visual feedback - Train->ggRightEndLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRightEndLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); + if( Train->ggRightEndLightButton.SubModel != nullptr ) { + Train->ggRightEndLightButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + else { + // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch + // this is crude, but for now will do + Train->ggRightLightButton.UpdateValue( 0.0, Train->dsbSwitch ); } -#endif } } } @@ -2594,24 +2271,12 @@ void TTrain::OnCommand_headlighttogglerearleft( TTrain *Train, command_data cons Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_right; // visual feedback Train->ggRearLeftLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearLeftLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_right; // visual feedback Train->ggRearLeftLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearLeftLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2636,24 +2301,12 @@ void TTrain::OnCommand_headlighttogglerearright( TTrain *Train, command_data con Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_left; // visual feedback Train->ggRearRightLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearRightLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_left; // visual feedback Train->ggRearRightLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearRightLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2677,24 +2330,12 @@ void TTrain::OnCommand_headlighttogglerearupper( TTrain *Train, command_data con Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_upper; // visual feedback Train->ggRearUpperLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearUpperLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::headlight_upper; // visual feedback Train->ggRearUpperLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearUpperLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2719,24 +2360,12 @@ void TTrain::OnCommand_redmarkertogglerearleft( TTrain *Train, command_data cons Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_right; // visual feedback Train->ggRearLeftEndLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearLeftEndLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_right; // visual feedback Train->ggRearLeftEndLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearLeftEndLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2761,24 +2390,12 @@ void TTrain::OnCommand_redmarkertogglerearright( TTrain *Train, command_data con Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_left; // visual feedback Train->ggRearRightEndLightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearRightEndLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } else { //turn off Train->DynamicObject->iLights[ lightsindex ] ^= TMoverParameters::light::redmarker_left; // visual feedback Train->ggRearRightEndLightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggRearRightEndLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif } } } @@ -2799,24 +2416,12 @@ void TTrain::OnCommand_headlightsdimtoggle( TTrain *Train, command_data const &C if( false == Train->DynamicObject->DimHeadlights ) { // turn on Train->DynamicObject->DimHeadlights = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggDimHeadlightsButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggDimHeadlightsButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { //turn off Train->DynamicObject->DimHeadlights = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggDimHeadlightsButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggDimHeadlightsButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2840,12 +2445,6 @@ void TTrain::OnCommand_interiorlighttoggle( TTrain *Train, command_data const &C // turn on Train->bCabLight = true; Train->btCabLight.TurnOn(); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggCabLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggCabLightButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -2853,12 +2452,6 @@ void TTrain::OnCommand_interiorlighttoggle( TTrain *Train, command_data const &C //turn off Train->bCabLight = false; Train->btCabLight.TurnOff(); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggCabLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggCabLightButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2880,24 +2473,12 @@ void TTrain::OnCommand_interiorlightdimtoggle( TTrain *Train, command_data const if( false == Train->bCabLightDim ) { // turn on Train->bCabLightDim = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggCabLightDimButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggCabLightDimButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { //turn off Train->bCabLightDim = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggCabLightDimButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggCabLightDimButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2921,24 +2502,12 @@ void TTrain::OnCommand_instrumentlighttoggle( TTrain *Train, command_data const if( false == Train->InstrumentLightActive ) { // turn on Train->InstrumentLightActive = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggInstrumentLightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggInstrumentLightButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { //turn off Train->InstrumentLightActive = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggInstrumentLightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggInstrumentLightButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2959,24 +2528,12 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command if( false == Train->mvControlled->Heating ) { // turn on Train->mvControlled->Heating = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggTrainHeatingButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggTrainHeatingButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { //turn off Train->mvControlled->Heating = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggTrainHeatingButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggTrainHeatingButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2999,23 +2556,11 @@ void TTrain::OnCommand_generictoggle( TTrain *Train, command_data const &Command // only reacting to press, so the switch doesn't flip back and forth if key is held down if( item.GetValue() < 0.25 ) { // turn on -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( item.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback item.UpdateValue( 1.0, Train->dsbSwitch ); } else { - //turn off -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( item.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif + // turn off // visual feedback item.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -3038,12 +2583,6 @@ void TTrain::OnCommand_doorlocktoggle( TTrain *Train, command_data const &Comman // TODO: check wheter we really need separate flags for this Train->mvControlled->DoorSignalling = true; Train->mvOccupied->DoorBlocked = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggDoorSignallingButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggDoorSignallingButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -3052,12 +2591,6 @@ void TTrain::OnCommand_doorlocktoggle( TTrain *Train, command_data const &Comman // TODO: check wheter we really need separate flags for this Train->mvControlled->DoorSignalling = false; Train->mvOccupied->DoorBlocked = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggDoorSignallingButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggDoorSignallingButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -3079,12 +2612,6 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman if( Train->mvOccupied->ActiveCab == 1 ) { if( Train->mvOccupied->DoorLeft( true ) ) { Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorLeftButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorOpen ); } } @@ -3092,12 +2619,6 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman // in the rear cab sides are reversed if( Train->mvOccupied->DoorRight( true ) ) { Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorRightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorOpen ); } } @@ -3107,12 +2628,6 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman if( Train->mvOccupied->ActiveCab == 1 ) { if( Train->mvOccupied->DoorLeft( false ) ) { Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorLeftButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorClose ); } } @@ -3120,12 +2635,6 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman // in the rear cab sides are reversed if( Train->mvOccupied->DoorRight( false ) ) { Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorRightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorClose ); } } @@ -3148,12 +2657,6 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma if( Train->mvOccupied->ActiveCab == 1 ) { if( Train->mvOccupied->DoorRight( true ) ) { Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorRightButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorOpen ); } } @@ -3161,12 +2664,6 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma // in the rear cab sides are reversed if( Train->mvOccupied->DoorLeft( true ) ) { Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorLeftButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorOpen ); } } @@ -3176,12 +2673,6 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma if( Train->mvOccupied->ActiveCab == 1 ) { if( Train->mvOccupied->DoorRight( false ) ) { Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorRightButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorClose ); } } @@ -3189,12 +2680,6 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma // in the rear cab sides are reversed if( Train->mvOccupied->DoorLeft( false ) ) { Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch ); -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // sound feedback - if( Train->ggDoorLeftButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif Train->play_sound( Train->dsbDoorClose ); } } @@ -3216,12 +2701,6 @@ void TTrain::OnCommand_departureannounce( TTrain *Train, command_data const &Com if( false == Train->mvControlled->DepartureSignal ) { // turn on Train->mvControlled->DepartureSignal = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggDepartureSignalButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggDepartureSignalButton.UpdateValue( 1.0, Train->dsbSwitch ); } @@ -3229,12 +2708,6 @@ void TTrain::OnCommand_departureannounce( TTrain *Train, command_data const &Com else if( Command.action == GLFW_RELEASE ) { // turn off Train->mvControlled->DepartureSignal = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggDepartureSignalButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggDepartureSignalButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -3261,13 +2734,6 @@ void TTrain::OnCommand_hornlowactivate( TTrain *Train, command_data const &Comma Train->mvControlled->WarningSignal &= ~2; } */ -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( ( Train->ggHornButton.GetValue() > -0.5 ) - || ( Train->ggHornLowButton.GetValue() < 0.5 ) ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggHornButton.UpdateValue( -1.0 ); Train->ggHornLowButton.UpdateValue( 1.0 ); @@ -3280,13 +2746,6 @@ void TTrain::OnCommand_hornlowactivate( TTrain *Train, command_data const &Comma Train->mvOccupied->WarningSignal &= ~( 1 | 2 ); */ Train->mvOccupied->WarningSignal &= ~1; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( ( Train->ggHornButton.GetValue() < -0.5 ) - || ( Train->ggHornLowButton.GetValue() > 0.5 ) ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggHornButton.UpdateValue( 0.0 ); Train->ggHornLowButton.UpdateValue( 0.0 ); @@ -3314,12 +2773,6 @@ void TTrain::OnCommand_hornhighactivate( TTrain *Train, command_data const &Comm Train->mvControlled->WarningSignal &= ~1; } */ -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggHornButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggHornButton.UpdateValue( 1.0 ); Train->ggHornHighButton.UpdateValue( 1.0 ); @@ -3332,12 +2785,6 @@ void TTrain::OnCommand_hornhighactivate( TTrain *Train, command_data const &Comm Train->mvOccupied->WarningSignal &= ~( 1 | 2 ); */ Train->mvOccupied->WarningSignal &= ~2; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggHornButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggHornButton.UpdateValue( 0.0 ); Train->ggHornHighButton.UpdateValue( 0.0 ); @@ -3361,38 +2808,33 @@ void TTrain::OnCommand_radiotoggle( TTrain *Train, command_data const &Command ) if( false == Train->mvOccupied->Radio ) { // turn on Train->mvOccupied->Radio = true; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggRadioButton.GetValue() < 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggRadioButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { // turn off Train->mvOccupied->Radio = false; -#ifdef EU07_USE_OLD_CONTROLSOUNDS - // audio feedback - if( Train->ggRadioButton.GetValue() > 0.5 ) { - Train->play_sound( Train->dsbSwitch ); - } -#endif // visual feedback Train->ggRadioButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } +void TTrain::OnCommand_radiostoptest( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + Train->Dynamic()->RadioStop(); + } +} + void TTrain::OnKeyDown(int cKey) { // naciśnięcie klawisza - bool isEztOer; - isEztOer = ((mvControlled->TrainType == dt_EZT) && (mvControlled->Battery == true) && - (mvControlled->EpFuse == true) && (mvOccupied->BrakeSubsystem == ss_ESt) && - (mvControlled->ActiveDir != 0)); // od yB - // isEztOer=(mvControlled->TrainType==dt_EZT)&&(mvControlled->Mains)&&(mvOccupied->BrakeSubsystem==ss_ESt)&&(mvControlled->ActiveDir!=0); - // isEztOer=((mvControlled->TrainType==dt_EZT)&&(mvControlled->Battery==true)&&(mvControlled->EpFuse==true)&&(mvOccupied->BrakeSubsystem==Oerlikon)&&(mvControlled->ActiveDir!=0)); + bool const isEztOer = + ( ( mvControlled->TrainType == dt_EZT ) + && ( mvControlled->Battery == true ) + && ( mvControlled->EpFuse == true ) + && ( mvOccupied->BrakeSubsystem == ss_ESt ) + && ( mvControlled->ActiveDir != 0 ) ); // od yB if (Global::shiftState) { // wciśnięty [Shift] @@ -3477,6 +2919,7 @@ void TTrain::OnKeyDown(int cKey) } else // McZapkie-240302 - klawisze bez shifta { +#ifdef EU07_USE_OLD_COMMAND_SYSTEM if( cKey == Global::Keys[ k_IncLocalBrakeLevel ] ) { // Ra 2014-09: w // trybie latania @@ -3489,10 +2932,8 @@ void TTrain::OnKeyDown(int cKey) { mvOccupied->IncManualBrakeLevel(1); } -#ifdef EU07_USE_OLD_COMMAND_SYSTEM else if (mvOccupied->LocalBrake != ManualBrake) mvOccupied->IncLocalBrakeLevel(1); -#endif } } else if (cKey == Global::Keys[k_DecLocalBrakeLevel]) @@ -3505,16 +2946,15 @@ void TTrain::OnKeyDown(int cKey) if (Global::ctrlState) if ((mvOccupied->LocalBrake == ManualBrake) || (mvOccupied->MBrake == true)) mvOccupied->DecManualBrakeLevel(1); -#ifdef EU07_USE_OLD_COMMAND_SYSTEM else // Ra 1014-06: AI potrafi zahamować pomocniczym mimo jego braku - // odhamować jakoś trzeba if ((mvOccupied->LocalBrake != ManualBrake) || mvOccupied->LocalBrakePos) mvOccupied->DecLocalBrakeLevel(1); -#endif } } - else if (cKey == Global::Keys[k_Brake2]) - { + else +#endif + if (cKey == Global::Keys[k_Brake2]) { if (Global::ctrlState) mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos(bh_NP)); // yB: czy ten stos funkcji nie powinien być jako oddzielna funkcja movera? @@ -5164,8 +4604,8 @@ bool TTrain::Update( double const Deltatime ) if (mvOccupied->BrakeHandle == FV4a) { double b = Console::AnalogCalibrateGet(0); - b = b * 8 - 2; - b = Global::CutValueToRange(-2.0, b, mvOccupied->BrakeCtrlPosNo); // przycięcie zmiennej do granic + b = b * 8.0 - 2.0; + b = clamp( b, -2.0, mvOccupied->BrakeCtrlPosNo ); // przycięcie zmiennej do granic if (Global::bMWDdebugEnable && Global::iMWDDebugMode & 4) WriteLog("FV4a break position = " + to_string(b)); ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); @@ -5173,8 +4613,8 @@ bool TTrain::Update( double const Deltatime ) if (mvOccupied->BrakeHandle == FVel6) // może można usunąć ograniczenie do FV4a i FVel6? { double b = Console::AnalogCalibrateGet(0); - b = b * 7 - 1; - b = Global::CutValueToRange(-1.0, b, mvOccupied->BrakeCtrlPosNo); // przycięcie zmiennej do granic + b = b * 7.0 - 1.0; + b = clamp( b, -1.0, mvOccupied->BrakeCtrlPosNo ); // przycięcie zmiennej do granic if (Global::bMWDdebugEnable && Global::iMWDDebugMode & 4) WriteLog("FVel6 break position = " + to_string(b)); ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); @@ -5197,8 +4637,8 @@ bool TTrain::Update( double const Deltatime ) if ((mvOccupied->BrakeLocHandle == FD1)) { double b = Console::AnalogCalibrateGet(1); - b *= 10; - b = Global::CutValueToRange(0.0, b, LocalBrakePosNo); // przycięcie zmiennej do granic + b *= 10.0; + b = clamp( b, 0.0, LocalBrakePosNo); // przycięcie zmiennej do granic ggLocalBrake.UpdateValue(b); // przesów bez zaokrąglenia if (Global::bMWDdebugEnable && Global::iMWDDebugMode & 4) WriteLog("FD1 break position = " + to_string(b)); mvOccupied->LocalBrakePos = @@ -7020,6 +6460,7 @@ void TTrain::clear_cab_controls() ggBrakeCtrl.Clear(); ggLocalBrake.Clear(); ggManualBrake.Clear(); + ggAlarmChain.Clear(); ggBrakeProfileCtrl.Clear(); ggBrakeProfileG.Clear(); ggBrakeProfileR.Clear(); @@ -7147,8 +6588,8 @@ void TTrain::clear_cab_controls() ggRearUpperLightButton.Clear(); ggRearLeftEndLightButton.Clear(); ggRearRightEndLightButton.Clear(); - btHaslerBrakes.Clear(12); // ciśnienie w cylindrach do odbijania na haslerze - btHaslerCurrent.Clear(13); // prąd na silnikach do odbijania na haslerze + btHaslerBrakes.Clear(12); // ciśnienie w cylindrach do odbijania na haslerze + btHaslerCurrent.Clear(13); // prąd na silnikach do odbijania na haslerze } // NOTE: we can get rid of this function once we have per-cab persistent state @@ -7248,10 +6689,20 @@ void TTrain::set_cab_controls() { ggUpperLightButton.PutValue( 1.0 ); } if( ( DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::redmarker_left ) != 0 ) { - ggLeftEndLightButton.PutValue( 1.0 ); + if( ggLeftEndLightButton.SubModel != nullptr ) { + ggLeftEndLightButton.PutValue( 1.0 ); + } + else { + ggLeftLightButton.PutValue( -1.0 ); + } } if( ( DynamicObject->iLights[ lightsindex ] & TMoverParameters::light::redmarker_right ) != 0 ) { - ggRightEndLightButton.PutValue( 1.0 ); + if( ggRightEndLightButton.SubModel != nullptr ) { + ggRightEndLightButton.PutValue( 1.0 ); + } + else { + ggRightLightButton.PutValue( -1.0 ); + } } if( true == DynamicObject->DimHeadlights ) { ggDimHeadlightsButton.PutValue( 1.0 ); @@ -7297,6 +6748,11 @@ void TTrain::set_cab_controls() { 1.0 : 0.0 ); } + // alarm chain + ggAlarmChain.PutValue( + mvControlled->AlarmChainFlag ? + 1.0 : + 0.0 ); // brake signalling ggSignallingButton.PutValue( mvControlled->Signalling ? @@ -7565,6 +7021,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "brakectrl:", ggBrakeCtrl }, { "localbrake:", ggLocalBrake }, { "manualbrake:", ggManualBrake }, + { "alarmchain:", ggAlarmChain }, { "brakeprofile_sw:", ggBrakeProfileCtrl }, { "brakeprofileg_sw:", ggBrakeProfileG }, { "brakeprofiler_sw:", ggBrakeProfileR }, diff --git a/Train.h b/Train.h index 2b4b4595..90a5ac6d 100644 --- a/Train.h +++ b/Train.h @@ -141,6 +141,9 @@ class TTrain static void OnCommand_trainbrakeservice( TTrain *Train, command_data const &Command ); static void OnCommand_trainbrakefullservice( TTrain *Train, command_data const &Command ); static void OnCommand_trainbrakeemergency( TTrain *Train, command_data const &Command ); + static void OnCommand_manualbrakeincrease( TTrain *Train, command_data const &Command ); + static void OnCommand_manualbrakedecrease( TTrain *Train, command_data const &Command ); + static void OnCommand_alarmchaintoggle( TTrain *Train, command_data const &Command ); static void OnCommand_wheelspinbrakeactivate( TTrain *Train, command_data const &Command ); static void OnCommand_sandboxactivate( TTrain *Train, command_data const &Command ); static void OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const &Command ); @@ -188,6 +191,7 @@ class TTrain static void OnCommand_hornlowactivate( TTrain *Train, command_data const &Command ); static void OnCommand_hornhighactivate( TTrain *Train, command_data const &Command ); static void OnCommand_radiotoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_radiostoptest( TTrain *Train, command_data const &Command ); static void OnCommand_generictoggle( TTrain *Train, command_data const &Command ); // members @@ -227,6 +231,7 @@ public: // reszta może by?publiczna TGauge ggBrakeCtrl; TGauge ggLocalBrake; TGauge ggManualBrake; + TGauge ggAlarmChain; TGauge ggBrakeProfileCtrl; // nastawiacz GPR - przelacznik obrotowy TGauge ggBrakeProfileG; // nastawiacz GP - hebelek towarowy TGauge ggBrakeProfileR; // nastawiacz PR - hamowanie dwustopniowe diff --git a/TrkFoll.cpp b/TrkFoll.cpp index 77c0adf4..7e9007d3 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -114,15 +114,20 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary) { // omijamy cały ten blok, gdy tor nie ma on żadnych eventów (większość nie ma) if (fDistance < 0) { - if (SetFlag(iEventFlag, -1)) // zawsze zeruje flagę sprawdzenia, jak mechanik - // dosiądzie, to się nie wykona - if (Owner->Mechanik->Primary()) // tylko dla jednego członu - // if (TestFlag(iEventFlag,1)) //McZapkie-280503: wyzwalanie event tylko dla - // pojazdow z obsada - if (bPrimary && pCurrentTrack->evEvent1 && - (!pCurrentTrack->evEvent1->iQueued)) - Global::AddToQuery(pCurrentTrack->evEvent1, Owner); // dodanie do - // kolejki + if( SetFlag( iEventFlag, -1 ) ) { + // zawsze zeruje flagę sprawdzenia, jak mechanik dosiądzie, to się nie wykona + if( ( Owner->Mechanik != nullptr ) + && ( Owner->Mechanik->Primary() ) ) { + // tylko dla jednego członu + // McZapkie-280503: wyzwalanie event tylko dla pojazdow z obsada + if( ( bPrimary ) + && ( pCurrentTrack->evEvent1 ) + && ( !pCurrentTrack->evEvent1->iQueued ) ) { + // dodanie do kolejki + Global::AddToQuery( pCurrentTrack->evEvent1, Owner ); + } + } + } // Owner->RaAxleEvent(pCurrentTrack->Event1); //Ra: dynamic zdecyduje, czy dodać do // kolejki // if (TestFlag(iEventallFlag,1)) @@ -136,31 +141,42 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary) } else if (fDistance > 0) { - if (SetFlag(iEventFlag, -2)) // zawsze ustawia flagę sprawdzenia, jak mechanik + if( SetFlag( iEventFlag, -2 ) ) { + // zawsze ustawia flagę sprawdzenia, jak mechanik // dosiądzie, to się nie wykona - if (Owner->Mechanik->Primary()) // tylko dla jednego członu - // if (TestFlag(iEventFlag,2)) //sprawdzanie jest od razu w pierwszym - // warunku - if (bPrimary && pCurrentTrack->evEvent2 && - (!pCurrentTrack->evEvent2->iQueued)) - Global::AddToQuery(pCurrentTrack->evEvent2, Owner); + if( ( Owner->Mechanik != nullptr ) + && ( Owner->Mechanik->Primary() ) ) { + // tylko dla jednego członu + if( ( bPrimary ) + && ( pCurrentTrack->evEvent2 ) + && ( !pCurrentTrack->evEvent2->iQueued ) ) { + Global::AddToQuery( pCurrentTrack->evEvent2, Owner ); + } + } + } // Owner->RaAxleEvent(pCurrentTrack->Event2); //Ra: dynamic zdecyduje, czy dodać do // kolejki // if (TestFlag(iEventallFlag,2)) - if (SetFlag(iEventallFlag, - -2)) // sprawdza i zeruje na przyszłość, true jeśli zmieni z 2 na 0 - if (bPrimary && pCurrentTrack->evEventall2 && - (!pCurrentTrack->evEventall2->iQueued)) - Global::AddToQuery(pCurrentTrack->evEventall2, Owner); + if( SetFlag( iEventallFlag, -2 ) ) { + // sprawdza i zeruje na przyszłość, true jeśli zmieni z 2 na 0 + if( ( bPrimary ) + && ( pCurrentTrack->evEventall2 ) + && ( !pCurrentTrack->evEventall2->iQueued ) ) { + Global::AddToQuery( pCurrentTrack->evEventall2, Owner ); + } + } // Owner->RaAxleEvent(pCurrentTrack->Eventall2); //Ra: dynamic zdecyduje, czy dodać // do kolejki } else // if (fDistance==0) //McZapkie-140602: wyzwalanie zdarzenia gdy pojazd stoi { - if (Owner->Mechanik->Primary()) // tylko dla jednego członu - if (pCurrentTrack->evEvent0) - if (!pCurrentTrack->evEvent0->iQueued) - Global::AddToQuery(pCurrentTrack->evEvent0, Owner); + if( ( Owner->Mechanik != nullptr ) + && ( Owner->Mechanik->Primary() ) ) { + // tylko dla jednego członu + if( pCurrentTrack->evEvent0 ) + if( !pCurrentTrack->evEvent0->iQueued ) + Global::AddToQuery( pCurrentTrack->evEvent0, Owner ); + } // Owner->RaAxleEvent(pCurrentTrack->Event0); //Ra: dynamic zdecyduje, czy dodać do // kolejki if (pCurrentTrack->evEventall0) diff --git a/World.cpp b/World.cpp index 363defe2..6612b6b0 100644 --- a/World.cpp +++ b/World.cpp @@ -288,17 +288,26 @@ bool TWorld::Init( GLFWwindow *Window ) { UILayer.set_background( "logo" ); - TSoundsManager::Init( glfwGetWin32Window( window ) ); - WriteLog("Sound Init OK"); + if( true == TSoundsManager::Init( glfwGetWin32Window( window ) ) ) { + WriteLog( "Sound subsystem setup complete" ); + } + else { + ErrorLog( "Sound subsystem setup failed" ); + return false; + } glfwSetWindowTitle( window, ( Global::AppName + " (" + Global::SceneryFile + ")" ).c_str() ); // nazwa scenerii UILayer.set_progress(0.01); UILayer.set_progress( "Loading scenery / Wczytywanie scenerii" ); GfxRenderer.Render(); - WriteLog( "Ground init" ); + WriteLog( "World setup..." ); if( true == Ground.Init( Global::SceneryFile ) ) { - WriteLog( "Ground init OK" ); + WriteLog( "...world setup done" ); + } + else { + ErrorLog( "...world setup failed" ); + return false; } simulation::Time.init(); @@ -1305,7 +1314,7 @@ TWorld::Update_UI() { else if( mover->ActiveDir < 0 ) { uitextline2 += " R"; } else { uitextline2 += " N"; } - uitextline3 = "Brakes:" + to_string( mover->fBrakeCtrlPos, 1, 5 ) + "+" + std::to_string( mover->LocalBrakePos ); + uitextline3 = "Brakes:" + to_string( mover->fBrakeCtrlPos, 1, 5 ) + "+" + std::to_string( mover->LocalBrakePos ) + ( mover->SlippingWheels ? " !" : " " ); if( Global::iScreenMode[ Global::iTextMode - GLFW_KEY_F1 ] == 1 ) { // detail mode on second key press @@ -1315,7 +1324,7 @@ TWorld::Update_UI() { + ", next limit: " + std::to_string( static_cast( std::floor( Controlled->Mechanik->VelNext ) ) ) + " km/h" + " in " + to_string( Controlled->Mechanik->ActualProximityDist * 0.001, 1 ) + " km)"; uitextline3 += - " Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa" + " Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa" + " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)"; } } @@ -1333,7 +1342,7 @@ TWorld::Update_UI() { if( tmp == nullptr ) { break; } // if the nearest located vehicle doesn't have a direct driver, try to query its owner auto const owner = ( - tmp->Mechanik != nullptr ? + ( ( tmp->Mechanik != nullptr ) && ( tmp->Mechanik->Primary() ) ) ? tmp->Mechanik : tmp->ctOwner ); if( owner == nullptr ){ break; } @@ -1425,13 +1434,19 @@ TWorld::Update_UI() { uitextline1 += "; C0:" + ( tmp->PrevConnected ? - tmp->PrevConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) : - "none" ); + tmp->PrevConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( + tmp->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ? + " (" + to_string( tmp->MoverParameters->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : + "" ) : + "none" ); uitextline1 += " C1:" + ( tmp->NextConnected ? - tmp->NextConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) : - "none" ); + tmp->NextConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( + tmp->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ? + " (" + to_string( tmp->MoverParameters->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : + "" ) : + "none" ); // equipment flags uitextline2 = ( tmp->MoverParameters->Battery ? "B" : "." ); @@ -1527,20 +1542,24 @@ TWorld::Update_UI() { */ if( tmp->Mechanik ) { // o ile jest ktoś w środku - std::string flags = "bwaccmlshhhoibsgvdp; "; // flagi AI (definicja w Driver.h) - for( int i = 0, j = 1; i < 19; ++i, j <<= 1 ) - if( tmp->Mechanik->DrivigFlags() & j ) // jak bit ustawiony - flags[ i + 1 ] = std::toupper( flags[ i + 1 ] ); // ^= 0x20; // to zmiana na wielką literę + std::string flags = "cpapcplhhndoiefgvdpseil "; // flagi AI (definicja w Driver.h) + for( int i = 0, j = 1; i < 23; ++i, j <<= 1 ) + if( false == ( tmp->Mechanik->DrivigFlags() & j ) ) // jak bit ustawiony + flags[ i ] = '.';// std::toupper( flags[ i ] ); // ^= 0x20; // to zmiana na wielką literę uitextline4 = flags; uitextline4 += "Driver: Vd=" + to_string( tmp->Mechanik->VelDesired, 0 ) - + " ad=" + to_string( tmp->Mechanik->AccDesired, 2 ) + + " Ad=" + to_string( tmp->Mechanik->AccDesired, 2 ) + + " Ah=" + to_string( tmp->Mechanik->fAccThreshold, 2 ) + + "@" + to_string( tmp->Mechanik->fBrake_a0[0], 2 ) + + "+" + to_string( tmp->Mechanik->fBrake_a1[0], 2 ) + + " Bd=" + to_string( tmp->Mechanik->fBrakeDist, 0 ) + " Pd=" + to_string( tmp->Mechanik->ActualProximityDist, 0 ) + " Vn=" + to_string( tmp->Mechanik->VelNext, 0 ) - + " VSm=" + to_string( tmp->Mechanik->VelSignalLast, 0 ) - + " VLm=" + to_string( tmp->Mechanik->VelLimitLast, 0 ) + + " VSl=" + to_string( tmp->Mechanik->VelSignalLast, 0 ) + + " VLl=" + to_string( tmp->Mechanik->VelLimitLast, 0 ) + " VRd=" + to_string( tmp->Mechanik->VelRoad, 0 ); if( ( tmp->Mechanik->VelNext == 0.0 ) @@ -1677,7 +1696,7 @@ TWorld::Update_UI() { + ")"; uitextline2 = - "HamZ=" + to_string( tmp->MoverParameters->fBrakeCtrlPos, 1 ) + "HamZ=" + to_string( tmp->MoverParameters->fBrakeCtrlPos, 2 ) + "; HamP=" + std::to_string( tmp->MoverParameters->LocalBrakePos ) + "/" + to_string( tmp->MoverParameters->LocalBrakePosA, 2 ) + "; NasJ=" + std::to_string( tmp->MoverParameters->MainCtrlPos ) + "(" + std::to_string( tmp->MoverParameters->MainCtrlActualPos ) + ")" + "; NasB=" + std::to_string( tmp->MoverParameters->ScndCtrlPos ) + "(" + std::to_string( tmp->MoverParameters->ScndCtrlActualPos ) + ")" @@ -1752,7 +1771,11 @@ TWorld::Update_UI() { && ( tmp->Mechanik->AIControllFlag == AIdriver ) ) { uitextline4 += "AI: Vd=" + to_string( tmp->Mechanik->VelDesired, 0 ) - + " ad=" + to_string( tmp->Mechanik->AccDesired, 2 ) + + " ad=" + to_string(tmp->Mechanik->AccDesired, 2) + + "/" + to_string(tmp->Mechanik->AccDesired*tmp->Mechanik->BrakeAccFactor(), 2) + + " atrain=" + to_string(tmp->Mechanik->fBrake_a0[0], 2) + + "+" + to_string(tmp->Mechanik->fBrake_a1[0], 2) + + " aS=" + to_string(tmp->Mechanik->AbsAccS_pub, 2) + " Pd=" + to_string( tmp->Mechanik->ActualProximityDist, 0 ) + " Vn=" + to_string( tmp->Mechanik->VelNext, 0 ); } diff --git a/color.h b/color.h index 998331a9..0a618426 100644 --- a/color.h +++ b/color.h @@ -47,9 +47,9 @@ RGBtoHSV( glm::vec3 const &RGB ) { hsv.x = ( RGB.g - RGB.b ) / delta; // between yellow & magenta else if( RGB.g >= max ) - hsv.x = 2.0 + ( RGB.g - RGB.r ) / delta; // between cyan & yellow + hsv.x = 2.f + ( RGB.g - RGB.r ) / delta; // between cyan & yellow else - hsv.x = 4.0 + ( RGB.r - RGB.g ) / delta; // between magenta & cyan + hsv.x = 4.f + ( RGB.r - RGB.g ) / delta; // between magenta & cyan hsv.x *= 60.0; // degrees @@ -76,9 +76,9 @@ HSVtoRGB( glm::vec3 const &HSV ) { hh /= 60.0; int const i = (int)hh; float const ff = hh - i; - float const p = HSV.z * ( 1.0 - HSV.y ); - float const q = HSV.z * ( 1.0 - ( HSV.y * ff ) ); - float const t = HSV.z * ( 1.0 - ( HSV.y * ( 1.0 - ff ) ) ); + float const p = HSV.z * ( 1.f - HSV.y ); + float const q = HSV.z * ( 1.f - ( HSV.y * ff ) ); + float const t = HSV.z * ( 1.f - ( HSV.y * ( 1.f - ff ) ) ); switch( i ) { case 0: diff --git a/command.cpp b/command.cpp index f297c6be..3f4b6f56 100644 --- a/command.cpp +++ b/command.cpp @@ -41,6 +41,9 @@ commanddescription_sequence Commands_descriptions = { { "trainbrakeservice", command_target::vehicle }, { "trainbrakefullservice", command_target::vehicle }, { "trainbrakeemergency", command_target::vehicle }, + { "manualbrakeincrease", command_target::vehicle }, + { "manualbrakedecrease", command_target::vehicle }, + { "alarmchaintoggle", command_target::vehicle }, { "wheelspinbrakeactivate", command_target::vehicle }, { "sandboxactivate", command_target::vehicle }, { "reverserincrease", command_target::vehicle }, @@ -62,6 +65,7 @@ commanddescription_sequence Commands_descriptions = { { "hornlowactivate", command_target::vehicle }, { "hornhighactivate", command_target::vehicle }, { "radiotoggle", command_target::vehicle }, + { "radiostoptest", command_target::vehicle }, /* const int k_FailedEngineCutOff = 35; */ @@ -118,10 +122,6 @@ const int k_ProgramHelp = 48; { "interiorlightdimtoggle", command_target::vehicle }, { "instrumentlighttoggle", command_target::vehicle }, /* -const int k_Univ1 = 66; -const int k_Univ2 = 67; -const int k_Univ3 = 68; -const int k_Univ4 = 69; const int k_EndSign = 70; const int k_Active = 71; */ diff --git a/command.h b/command.h index 239b145e..a06509e5 100644 --- a/command.h +++ b/command.h @@ -36,6 +36,9 @@ enum class user_command { trainbrakeservice, trainbrakefullservice, trainbrakeemergency, + manualbrakeincrease, + manualbrakedecrease, + alarmchaintoggle, wheelspinbrakeactivate, sandboxactivate, reverserincrease, @@ -57,6 +60,7 @@ enum class user_command { hornlowactivate, hornhighactivate, radiotoggle, + radiostoptest, /* const int k_FailedEngineCutOff = 35; */ diff --git a/keyboardinput.cpp b/keyboardinput.cpp index 959d3648..cc7041c4 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -208,6 +208,12 @@ keyboard_input::default_bindings() { { GLFW_KEY_KP_2 }, // trainbrakeemergency { GLFW_KEY_KP_0 }, + // manualbrakeincrease + { GLFW_KEY_KP_1 | keymodifier::control }, + // manualbrakedecrease + { GLFW_KEY_KP_7 | keymodifier::control }, + // alarm chain toggle + { GLFW_KEY_B | keymodifier::shift | keymodifier::control }, // wheelspinbrakeactivate, { GLFW_KEY_KP_ENTER }, // sandboxactivate, @@ -250,6 +256,8 @@ keyboard_input::default_bindings() { { GLFW_KEY_A | keymodifier::shift }, // radiotoggle { GLFW_KEY_R | keymodifier::control }, + // radiostoptest + { GLFW_KEY_R | keymodifier::shift | keymodifier::control }, // viewturn { -1 }, // movevector diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index c7fd57a7..4e8b1c80 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -228,6 +228,9 @@ Source Files + + Source Files + @@ -443,6 +446,9 @@ Header Files + + Header Files + diff --git a/material.cpp b/material.cpp new file mode 100644 index 00000000..c93ca908 --- /dev/null +++ b/material.cpp @@ -0,0 +1,143 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" + +#include "material.h" +#include "renderer.h" +#include "usefull.h" +#include "globals.h" + +bool +opengl_material::deserialize( cParser &Input, bool const Loadnow ) { + + bool result { false }; + while( true == deserialize_mapping( Input, Loadnow ) ) { + result = true; // once would suffice but, eh + } + + has_alpha = ( + texture1 != null_handle ? + GfxRenderer.Texture( texture1 ).has_alpha : + false ); + + return result; +} + +// imports member data pair from the config file +bool +opengl_material::deserialize_mapping( cParser &Input, bool const Loadnow ) { + + if( false == Input.getTokens( 2, true, "\n\r\t;, " ) ) { + return false; + } + + std::string path; + if( name.rfind( '\\' ) != std::string::npos ) { + path = name.substr( 0, name.rfind( '\\' ) + 1 ); + } + + std::string key, value; + Input + >> key + >> value; + + if( key == "texture1:" ) { texture1 = GfxRenderer.Fetch_Texture( path + value, Loadnow ); } + else if( key == "texture2:" ) { texture2 = GfxRenderer.Fetch_Texture( path + value, Loadnow ); } + + return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized +} + + +// create material object from data stored in specified file. +// NOTE: the deferred load parameter is passed to textures defined by material, the material itself is always loaded immediately +material_handle +material_manager::create( std::string const &Filename, bool const Loadnow ) { + + auto filename { Filename }; + + if( filename.find( '|' ) != std::string::npos ) + filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury + + if( filename.rfind( '.' ) != std::string::npos ) { + // we can get extension for .mat or, in legacy files, some image format. just trim it and set it to material file extension + filename.erase( filename.rfind( '.' ) ); + } + filename += ".mat"; + + for( char &c : filename ) { + // change forward slashes to windows ones. NOTE: probably not strictly necessary, but eh + c = ( c == '/' ? '\\' : c ); + } + if( filename.find( '\\' ) == std::string::npos ) { + // jeśli bieżaca ścieżka do tekstur nie została dodana to dodajemy domyślną + filename = szTexturePath + filename; + } + + // try to locate requested material in the databank + auto const databanklookup = find_in_databank( filename ); + if( databanklookup != npos ) { + return databanklookup; + } + // if this fails, try to look for it on disk + opengl_material material; + material.name = filename; + auto const disklookup = find_on_disk( filename ); + if( disklookup != "" ) { + cParser materialparser( disklookup, cParser::buffer_FILE ); + if( false == material.deserialize( materialparser, Loadnow ) ) { + // deserialization failed but the .mat file does exist, so we give up at this point + return null_handle; + } + } + else { + // if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case + material.texture1 = GfxRenderer.Fetch_Texture( Filename, Loadnow ); + if( material.texture1 == null_handle ) { + // if there's also no texture, give up + return null_handle; + } + material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha; + } + + material_handle handle = m_materials.size(); + m_materials.emplace_back( material ); + m_materialmappings.emplace( material.name, handle ); + return handle; +}; + +// checks whether specified texture is in the texture bank. returns texture id, or npos. +material_handle +material_manager::find_in_databank( std::string const &Materialname ) const { + + auto lookup = m_materialmappings.find( Materialname ); + if( lookup != m_materialmappings.end() ) { + return lookup->second; + } + // jeszcze próba z dodatkową ścieżką + lookup = m_materialmappings.find( szTexturePath + Materialname ); + + return ( + lookup != m_materialmappings.end() ? + lookup->second : + npos ); +} + +// checks whether specified file exists. +// NOTE: this is direct copy of the method used by texture manager. TBD, TODO: refactor into common routine? +std::string +material_manager::find_on_disk( std::string const &Materialname ) const { + + return( + FileExists( Materialname ) ? Materialname : + FileExists( szTexturePath + Materialname ) ? szTexturePath + Materialname : + "" ); +} + +//--------------------------------------------------------------------------- diff --git a/material.h b/material.h new file mode 100644 index 00000000..841d1861 --- /dev/null +++ b/material.h @@ -0,0 +1,64 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "texture.h" +#include "parser.h" + +typedef int material_handle; + +// a collection of parameters for the rendering setup. +// for modern opengl this translates to set of attributes for the active shaders, +// for legacy opengl this is basically just texture(s) assigned to geometry +struct opengl_material { + + texture_handle texture1 { null_handle }; // primary texture, typically diffuse+apha + texture_handle texture2 { null_handle }; // secondary texture, typically normal+reflection + + bool has_alpha { false }; // alpha state, calculated from presence of alpha in texture1 + std::string name; +// methods: + bool + deserialize( cParser &Input, bool const Loadnow ); +private: + // imports member data pair from the config file + bool + deserialize_mapping( cParser &Input, bool const Loadnow ); +}; + +class material_manager { + +public: + material_manager() { m_materials.emplace_back( opengl_material() ); } // empty bindings for null material + + material_handle + create( std::string const &Filename, bool const Loadnow ); + opengl_material const & + material( material_handle const Material ) const { return m_materials[ Material ]; } + +private: +// types + typedef std::vector material_sequence; + typedef std::unordered_map index_map; +// methods: + // checks whether specified texture is in the texture bank. returns texture id, or npos. + material_handle + find_in_databank( std::string const &Materialname ) const; + // checks whether specified file exists. returns name of the located file, or empty string. + std::string + find_on_disk( std::string const &Materialname ) const; +// members: + material_handle const npos { -1 }; + material_sequence m_materials; + index_map m_materialmappings; + +}; + +//--------------------------------------------------------------------------- diff --git a/moon.cpp b/moon.cpp index 02db79f1..d4da4c38 100644 --- a/moon.cpp +++ b/moon.cpp @@ -37,8 +37,8 @@ cMoon::update() { move(); glm::vec3 position( 0.f, 0.f, -2000.f * Global::fDistanceFactor ); - position = glm::rotateX( position, glm::radians( m_body.elevref ) ); - position = glm::rotateY( position, glm::radians( -m_body.hrang ) ); + position = glm::rotateX( position, glm::radians( static_cast( m_body.elevref ) ) ); + position = glm::rotateY( position, glm::radians( static_cast( -m_body.hrang ) ) ); m_position = position; } @@ -46,17 +46,16 @@ cMoon::update() { void cMoon::render() { - glColor4f( 225.0f/255.0f, 225.0f/255.0f, 255.0f/255.0f, 1.f ); - // debug line to locate the sun easier - Math3D::vector3 position = m_position; - glBegin( GL_LINES ); - glVertex3f( position.x, position.y, position.z ); - glVertex3f( position.x, 0.0f, position.z ); - glEnd(); - glPushMatrix(); - glTranslatef( position.x, position.y, position.z ); - gluSphere( moonsphere, /* (float)( Global::iWindowHeight / Global::FieldOfView ) * 0.5 * */ ( m_body.distance / 60.2666 ) * 9.037461, 12, 12 ); - glPopMatrix(); + ::glColor4f( 225.f / 255.f, 225.f / 255.f, 255.f / 255.f, 1.f ); + // debug line to locate the moon easier + ::glBegin( GL_LINES ); + ::glVertex3fv( glm::value_ptr( m_position ) ); + ::glVertex3f( m_position.x, 0.f, m_position.z ); + ::glEnd(); + ::glPushMatrix(); + ::glTranslatef( m_position.x, m_position.y, m_position.z ); + ::gluSphere( moonsphere, /* (float)( Global::iWindowHeight / Global::FieldOfView ) * 0.5 * */ ( m_body.distance / 60.2666 ) * 9.037461, 12, 12 ); + ::glPopMatrix(); } glm::vec3 @@ -78,8 +77,8 @@ float cMoon::getIntensity() { // calculating intensity of the sun instead, and returning 15% of the value, // which roughly matches how much sunlight is reflected by the moon // We alter the intensity further based on current phase of the moon - auto const phasefactor = 1.0f - std::abs( m_phase - 29.53f * 0.5f ) / ( 29.53 * 0.5f ); - return (float)( m_body.etr/ 1399.0 ) * phasefactor * 0.15f; // arbitrary scaling factor taken from etrn value + auto const phasefactor = 1.0f - std::abs( m_phase - 29.53f * 0.5f ) / ( 29.53f * 0.5f ); + return static_cast( ( m_body.etr/ 1399.0 ) * phasefactor * 0.15 ); // arbitrary scaling factor taken from etrn value } void cMoon::setLocation( float const Longitude, float const Latitude ) { diff --git a/mouseinput.cpp b/mouseinput.cpp index e5b8c1de..3709740c 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -211,7 +211,10 @@ mouse_input::default_bindings() { user_command::independentbrakeincrease, user_command::independentbrakedecrease } }, { "manualbrake:", { - user_command::none, + user_command::manualbrakeincrease, + user_command::manualbrakedecrease } }, + { "alarmchain:", { + user_command::alarmchaintoggle, user_command::none } }, { "brakeprofile_sw:", { user_command::brakeactingspeedincrease, diff --git a/mtable.cpp b/mtable.cpp index 7481c9a0..34faea9f 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -131,7 +131,6 @@ void Mtable::TTrainParameters::RewindTimeTable(std::string actualStationName) } } - void TTrainParameters::StationIndexInc() { // przejście do następnej pozycji StationIndex<=StationCount ++StationIndex; diff --git a/mtable.h b/mtable.h index 59c965a2..f5471f81 100644 --- a/mtable.h +++ b/mtable.h @@ -76,7 +76,7 @@ class TTrainParameters bool IsTimeToGo(double hh, double mm); bool UpdateMTable(double hh, double mm, std::string const &NewName); bool UpdateMTable( simulation_time const &Time, std::string const &NewName ); - void RewindTimeTable(std::string actualStationName); + void RewindTimeTable( std::string actualStationName ); TTrainParameters( std::string const &NewTrainName ); void NewName(std::string const &NewTrainName); void UpdateVelocity(int StationCount, double vActual); diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 0a046ead..d1e47062 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -118,8 +118,9 @@ geometry_bank::vertices( geometry_handle const &Geometry ) const { // opengl vbo-based variant of the geometry bank -GLuint opengl_vbogeometrybank::m_activebuffer { NULL }; // buffer bound currently on the opengl end, if any +GLuint opengl_vbogeometrybank::m_activebuffer { 0 }; // buffer bound currently on the opengl end, if any unsigned int opengl_vbogeometrybank::m_activestreams { stream::none }; // currently enabled data type pointers +std::vector opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays // create() subclass details void @@ -150,7 +151,7 @@ opengl_vbogeometrybank::replace_( geometry_handle const &Geometry ) { void opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ) { - if( m_buffer == NULL ) { + if( m_buffer == 0 ) { // if there's no buffer, we'll have to make one // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste @@ -234,14 +235,14 @@ opengl_vbogeometrybank::bind_buffer() { void opengl_vbogeometrybank::delete_buffer() { - if( m_buffer != NULL ) { + if( m_buffer != 0 ) { ::glDeleteBuffers( 1, &m_buffer ); if( m_activebuffer == m_buffer ) { - m_activebuffer = NULL; + m_activebuffer = 0; release_streams(); } - m_buffer = NULL; + m_buffer = 0; m_buffercapacity = 0; // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice @@ -274,12 +275,19 @@ opengl_vbogeometrybank::bind_streams( stream_units const &Units, unsigned int co ::glDisableClientState( GL_COLOR_ARRAY ); } if( Streams & stream::texture ) { - ::glClientActiveTexture( Units.texture ); - ::glTexCoordPointer( 2, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + 24 ); - ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + for( auto unit : Units.texture ) { + ::glClientActiveTexture( unit ); + ::glTexCoordPointer( 2, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + 24 ); + ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + } + m_activetexturearrays = Units.texture; } else { - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + for( auto unit : Units.texture ) { + ::glClientActiveTexture( unit ); + ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + m_activetexturearrays.clear(); // NOTE: we're simplifying here, since we always toggle the same texture coord sets } m_activestreams = Streams; @@ -291,9 +299,13 @@ opengl_vbogeometrybank::release_streams() { ::glDisableClientState( GL_VERTEX_ARRAY ); ::glDisableClientState( GL_NORMAL_ARRAY ); ::glDisableClientState( GL_COLOR_ARRAY ); - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + for( auto unit : m_activetexturearrays ) { + ::glClientActiveTexture( unit ); + ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } m_activestreams = stream::none; + m_activetexturearrays.clear(); } // opengl display list based variant of the geometry bank @@ -331,7 +343,7 @@ opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, stream_units cons for( auto const &vertex : chunk.vertices ) { if( Streams & stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } else if( Streams & stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } - if( Streams & stream::texture ) { ::glMultiTexCoord2fv( Units.texture, glm::value_ptr( vertex.texture ) ); } + if( Streams & stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } if( Streams & stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } } ::glEnd(); @@ -411,7 +423,7 @@ geometrybank_manager::append( vertex_array &Vertices, geometry_handle const &Geo void geometrybank_manager::draw( geometry_handle const &Geometry, unsigned int const Streams ) { - if( Geometry == NULL ) { return; } + if( Geometry == null_handle ) { return; } auto &bankrecord = bank( Geometry ); diff --git a/openglgeometrybank.h b/openglgeometrybank.h index 90bec5a0..ee0aa234 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -46,7 +46,7 @@ unsigned int const color_streams { stream::position | stream::color | stream::te struct stream_units { - GLint texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream + std::vector texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream }; typedef std::vector vertex_array; @@ -207,7 +207,8 @@ private: // members: static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any static unsigned int m_activestreams; - GLuint m_buffer { NULL }; // id of the buffer holding data on the opengl end + static std::vector m_activetexturearrays; + GLuint m_buffer { 0 }; // id of the buffer holding data on the opengl end std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order diff --git a/openglmatrixstack.h b/openglmatrixstack.h index 27a875ab..4d988b8a 100644 --- a/openglmatrixstack.h +++ b/openglmatrixstack.h @@ -27,7 +27,7 @@ class opengl_stack { public: // constructors: - opengl_stack() { m_stack.emplace(1.0f); } + opengl_stack() { m_stack.emplace(1.f); } // methods: glm::mat4 const & @@ -86,29 +86,32 @@ private: mat4_stack m_stack; }; -enum stack_mode { gl_projection = 0, gl_modelview = 1 }; +enum stack_mode { gl_modelview = 0, gl_projection = 1, gl_texture = 2 }; typedef std::vector openglstack_array; public: // constructors: opengl_matrices() { - m_stacks.emplace_back(); // projection m_stacks.emplace_back(); // modelview + m_stacks.emplace_back(); // projection + m_stacks.emplace_back(); // texture } // methods: void mode( GLuint const Mode ) { switch( Mode ) { - case GL_PROJECTION: { m_mode = stack_mode::gl_projection; break; } case GL_MODELVIEW: { m_mode = stack_mode::gl_modelview; break; } + case GL_PROJECTION: { m_mode = stack_mode::gl_projection; break; } + case GL_TEXTURE: { m_mode = stack_mode::gl_texture; break; } default: { break; } } - glMatrixMode( Mode ); } + ::glMatrixMode( Mode ); } glm::mat4 const & data( GLuint const Mode = -1 ) const { switch( Mode ) { - case GL_PROJECTION: { return m_stacks[ stack_mode::gl_projection ].data(); } case GL_MODELVIEW: { return m_stacks[ stack_mode::gl_modelview ].data(); } + case GL_PROJECTION: { return m_stacks[ stack_mode::gl_projection ].data(); } + case GL_TEXTURE: { return m_stacks[ stack_mode::gl_texture ].data(); } default: { return m_stacks[ m_mode ].data(); } } } float const * data_array( GLuint const Mode = -1 ) const { @@ -128,7 +131,7 @@ public: void rotate( Type_ const Angle, Type_ const X, Type_ const Y, Type_ const Z ) { m_stacks[ m_mode ].rotate( - static_cast(Angle) * 0.0174532925f, // deg2rad + static_cast(glm::radians(Angle)), glm::vec3( static_cast(X), static_cast(Y), diff --git a/parser.cpp b/parser.cpp index c7bbebd5..465b1d7b 100644 --- a/parser.cpp +++ b/parser.cpp @@ -21,32 +21,32 @@ http://mozilla.org/MPL/2.0/. // cParser -- generic class for parsing text data. // constructors -cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction ) -{ - LoadTraction = Loadtraction; +cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction ) : + mPath(Path), + LoadTraction( Loadtraction ) { // build comments map mComments.insert(commentmap::value_type("/*", "*/")); mComments.insert(commentmap::value_type("//", "\n")); // mComments.insert(commentmap::value_type("--","\n")); //Ra: to chyba nie używane // store to calculate sub-sequent includes from relative path - mPath = Path; if( Type == buffertype::buffer_FILE ) { mFile = Stream; } // reset pointers and attach proper type of buffer - switch (Type) - { - case buffer_FILE: - Path.append(Stream); - mStream = new std::ifstream(Path.c_str()); - break; - case buffer_TEXT: - mStream = new std::istringstream(Stream); - break; - default: - mStream = NULL; + switch (Type) { + case buffer_FILE: { + Path.append( Stream ); + mStream = std::make_shared( Path ); + break; + } + case buffer_TEXT: { + mStream = std::make_shared( Stream ); + break; + } + default: { + break; + } } - mIncludeParser = NULL; // calculate stream size if (mStream) { @@ -56,19 +56,14 @@ cParser::cParser( std::string const &Stream, buffertype const Type, std::string else { mSize = mStream->rdbuf()->pubseekoff( 0, std::ios_base::end ); mStream->rdbuf()->pubseekoff( 0, std::ios_base::beg ); + mLine = 1; } } - else - mSize = 0; } // destructor -cParser::~cParser() -{ - if (mIncludeParser) - delete mIncludeParser; - if (mStream) - delete mStream; +cParser::~cParser() { + mComments.clear(); } @@ -157,7 +152,7 @@ std::string cParser::readToken(bool ToLower, const char *Break) // see if there's include parsing going on. clean up when it's done. if (mIncludeParser) { - token = (*mIncludeParser).readToken(ToLower, Break); + token = mIncludeParser->readToken(ToLower, Break); if (!token.empty()) { pos = token.find("(p"); @@ -182,13 +177,12 @@ std::string cParser::readToken(bool ToLower, const char *Break) } else { - delete mIncludeParser; mIncludeParser = NULL; parameters.clear(); } } // get the token yourself if there's no child to delegate it to. - char c; + char c { 0 }; do { while (mStream->peek() != EOF && strchr(Break, c = mStream->get()) == NULL) @@ -201,6 +195,10 @@ std::string cParser::readToken(bool ToLower, const char *Break) if (trimComments(token)) // don't glue together words separated with comment break; } + if( c == '\n' ) { + // update line counter + ++mLine; + } } while (token == "" && mStream->peek() != EOF); // double check to deal with trailing spaces // launch child parser if include directive found. // NOTE: parameter collecting uses default set of token separators. @@ -218,16 +216,18 @@ std::string cParser::readToken(bool ToLower, const char *Break) && (parameter.compare("end") != 0) ) { parameters.push_back(parameter); - parameter = readToken(ToLower); + parameter = readToken(false); } // if (trtest2.find("tr/")!=0) - mIncludeParser = new cParser(includefile, buffer_FILE, mPath, LoadTraction); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction); if (mIncludeParser->mSize <= 0) ErrorLog("Missed include: " + includefile); } - else - while (token.compare("end") != 0) - token = readToken(ToLower); + else { + while( token.compare( "end" ) != 0 ) { + token = readToken( true ); // minimize risk of case mismatch on comparison + } + } token = readToken(ToLower, Break); } return token; @@ -235,8 +235,12 @@ std::string cParser::readToken(bool ToLower, const char *Break) std::string cParser::readQuotes(char const Quote) { // read the stream until specified char or stream end std::string token = ""; - char c; + char c { 0 }; while( mStream->peek() != EOF && Quote != (c = mStream->get()) ) { // get all chars until the quote mark + if( c == '\n' ) { + // update line counter + ++mLine; + } token += c; } return token; @@ -244,9 +248,16 @@ std::string cParser::readQuotes(char const Quote) { // read the stream until spe void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca std::string input = ""; + char c { 0 }; auto const endmarksize = Endmark.size(); - while( mStream->peek() != EOF ) { // o ile nie koniec pliku - input += mStream->get(); // pobranie znaku + while( mStream->peek() != EOF ) { + // o ile nie koniec pliku + c = mStream->get(); // pobranie znaku + if( c == '\n' ) { + // update line counter + ++mLine; + } + input += c; if( input.find( Endmark ) != std::string::npos ) // szukanie znacznika końca break; if( input.size() >= endmarksize ) { @@ -302,7 +313,7 @@ std::size_t cParser::countTokens( std::string const &Stream, std::string Path ) std::size_t cParser::count() { std::string token; - size_t count{ 0 }; + size_t count { 0 }; do { token = ""; token = readToken( false ); @@ -319,8 +330,16 @@ void cParser::addCommentStyle( std::string const &Commentstart, std::string cons // returns name of currently open file, or empty string for text type stream std::string -cParser::Name() { +cParser::Name() const { if( mIncludeParser ) { return mIncludeParser->Name(); } else { return mPath + mFile; } } + +// returns number of currently processed line +std::size_t +cParser::Line() const { + + if( mIncludeParser ) { return mIncludeParser->Line(); } + else { return mLine; } +} diff --git a/parser.h b/parser.h index 982959ef..5709d16c 100644 --- a/parser.h +++ b/parser.h @@ -78,7 +78,9 @@ class cParser //: public std::stringstream // add custom definition of text which should be ignored when retrieving tokens void addCommentStyle( std::string const &Commentstart, std::string const &Commentend ); // returns name of currently open file, or empty string for text type stream - std::string Name(); + std::string Name() const; + // returns number of currently processed line + std::size_t Line() const; private: // methods: @@ -90,13 +92,14 @@ class cParser //: public std::stringstream std::size_t count(); // members: bool LoadTraction; // load traction? - std::istream *mStream; // relevant kind of buffer is attached on creation. + std::shared_ptr mStream; // relevant kind of buffer is attached on creation. std::string mFile; // name of the open file, if any std::string mPath; // path to open stream, for relative path lookups. - std::streamoff mSize; // size of open stream, for progress report. + std::streamoff mSize { 0 }; // size of open stream, for progress report. + std::size_t mLine { 0 }; // currently processed line typedef std::map commentmap; commentmap mComments; - cParser *mIncludeParser; // child class to handle include directives. + std::shared_ptr mIncludeParser; // child class to handle include directives. std::vector parameters; // parameter list for included file. std::deque tokens; }; diff --git a/renderer.cpp b/renderer.cpp index e55a781d..530e4c25 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -26,6 +26,7 @@ opengl_renderer GfxRenderer; extern TWorld World; int const EU07_PICKBUFFERSIZE { 1024 }; // size of (square) textures bound with the pick framebuffer +int const EU07_ENVIRONMENTBUFFERSIZE { 256 }; // size of (square) environmental cube map texture namespace colors { @@ -98,9 +99,13 @@ opengl_renderer::Init( GLFWwindow *Window ) { glEnable( GL_CULL_FACE ); // Cull back-facing triangles glShadeModel( GL_SMOOTH ); // Enable Smooth Shading - glActiveTexture( m_diffusetextureunit ); - m_geometry.units().texture = m_diffusetextureunit; + m_geometry.units().texture = ( + Global::BasicRenderer ? + std::vector{ m_diffusetextureunit } : + std::vector{ m_normaltextureunit, m_diffusetextureunit } ); + m_textures.assign_units( m_helpertextureunit, m_shadowtextureunit, m_normaltextureunit, m_diffusetextureunit ); // TODO: add reflections unit UILayer.set_unit( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); ::glDepthFunc( GL_LEQUAL ); glEnable( GL_DEPTH_TEST ); @@ -172,11 +177,11 @@ opengl_renderer::Init( GLFWwindow *Window ) { // texture ::glGenTextures( 1, &m_picktexture ); ::glBindTexture( GL_TEXTURE_2D, m_picktexture ); + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); // depth buffer ::glGenRenderbuffersEXT( 1, &m_pickdepthbuffer ); ::glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_pickdepthbuffer ); @@ -245,12 +250,47 @@ opengl_renderer::Init( GLFWwindow *Window ) { } else { ErrorLog( "Shadows framebuffer setup failed" ); - m_framebuffersupport = false; Global::RenderShadows = false; } ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now } - + // environment cube map resources + if( ( false == Global::BasicRenderer ) + && ( true == m_framebuffersupport ) ) { + // texture: + ::glGenTextures( 1, &m_environmentcubetexture ); + ::glBindTexture( GL_TEXTURE_CUBE_MAP, m_environmentcubetexture ); + // allocate space + for( GLuint faceindex = GL_TEXTURE_CUBE_MAP_POSITIVE_X; faceindex < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6; ++faceindex ) { + ::glTexImage2D( faceindex, 0, GL_RGBA8, EU07_ENVIRONMENTBUFFERSIZE, EU07_ENVIRONMENTBUFFERSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); + } + // setup parameters + ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + // depth buffer + ::glGenRenderbuffersEXT( 1, &m_environmentdepthbuffer ); + ::glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_environmentdepthbuffer ); + ::glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, EU07_ENVIRONMENTBUFFERSIZE, EU07_ENVIRONMENTBUFFERSIZE ); + // create and assemble the framebuffer + ::glGenFramebuffersEXT( 1, &m_environmentframebuffer ); + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_environmentframebuffer ); + ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, m_environmentcubetexture, 0 ); + ::glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_environmentdepthbuffer ); + // check if we got it working + GLenum status = ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); + if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { + WriteLog( "Reflections framebuffer setup complete" ); + m_environmentcubetexturesupport = true; + } + else { + ErrorLog( "Reflections framebuffer setup failed" ); + m_environmentcubetexturesupport = false; + } + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now + } // prepare basic geometry chunks auto const geometrybank = m_geometry.create_bank(); float const size = 2.5f; @@ -280,6 +320,7 @@ opengl_renderer::Render() { m_renderpass.draw_mode = rendermode::none; // force setup anew m_debuginfo.clear(); + ++m_framestamp; Render_pass( rendermode::color ); m_drawcount = m_drawqueue.size(); @@ -336,6 +377,14 @@ opengl_renderer::Render_pass( rendermode const Mode ) { glm::vec3{ m_renderpass.camera.position() - shadowcamera.position() } ); } + if( ( true == m_environmentcubetexturesupport ) + && ( true == World.InitPerformed() ) ) { + // potentially update environmental cube map + if( true == Render_reflections() ) { + setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack + } + } + ::glViewport( 0, 0, Global::iWindowWidth, Global::iWindowHeight ); if( World.InitPerformed() ) { @@ -382,6 +431,15 @@ opengl_renderer::Render_pass( rendermode const Mode ) { switch_units( true, false, false ); // cab render is done in translucent phase to deal with badly configured vehicles if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } + + if( m_environmentcubetexturesupport ) { + // restore default texture matrix for reflections cube map + Active_Texture( m_helpertextureunit ); + ::glMatrixMode( GL_TEXTURE ); + ::glPopMatrix(); + Active_Texture( m_diffusetextureunit ); + ::glMatrixMode( GL_MODELVIEW ); + } } UILayer.render(); break; @@ -393,7 +451,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // setup auto const shadowdrawstart = std::chrono::steady_clock::now(); - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); + ::glBindFramebufferEXT( GL_FRAMEBUFFER, m_shadowframebuffer ); ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); @@ -430,6 +488,39 @@ opengl_renderer::Render_pass( rendermode const Mode ) { break; } + case rendermode::reflections: { + // NOTE: buffer and viewport setup in this mode is handled by the wrapper method + ::glClearColor( 0.f, 0.f, 0.f, 1.f ); + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + if( World.InitPerformed() ) { + + ::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE ); + // setup + setup_matrices(); + // render + setup_drawing( true ); + setup_units( true, false, false ); + Render( &World.Environment ); + // opaque parts... + setup_drawing( false ); + setup_units( true, true, true ); + Render( &World.Ground ); +/* + // reflections are limited to sky and ground only, the update rate is too low for anything else + // ...translucent parts + setup_drawing( true ); + Render_Alpha( &World.Ground ); + // cab render is performed without shadows, due to low resolution and number of models without windows :| + switch_units( true, false, false ); + // cab render is done in translucent phase to deal with badly configured vehicles + if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } +*/ + ::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + } + break; + } + case rendermode::pickcontrols: { if( World.InitPerformed() ) { // setup @@ -479,6 +570,34 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } } +// creates dynamic environment cubemap +bool +opengl_renderer::Render_reflections() { + + auto const &time = simulation::Time.data(); + auto const timestamp = time.wDay * 60 * 24 + time.wHour * 60 + time.wMinute; + if( ( timestamp - m_environmentupdatetime < 5 ) + && ( glm::length( m_renderpass.camera.position() - m_environmentupdatelocation ) < 1000.0 ) ) { + // run update every 5+ mins of simulation time, or at least 1km from the last location + return false; + } + m_environmentupdatetime = timestamp; + m_environmentupdatelocation = m_renderpass.camera.position(); + + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_environmentframebuffer ); + ::glViewport( 0, 0, EU07_ENVIRONMENTBUFFERSIZE, EU07_ENVIRONMENTBUFFERSIZE ); + for( m_environmentcubetextureface = GL_TEXTURE_CUBE_MAP_POSITIVE_X; + m_environmentcubetextureface < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6; + ++m_environmentcubetextureface ) { + + ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, m_environmentcubetextureface, m_environmentcubetexture, 0 ); + Render_pass( rendermode::reflections ); + } + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + + return true; +} + void opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear, float const Zfar, bool const Ignoredebug ) { @@ -489,6 +608,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f switch( Mode ) { case rendermode::color: { Config.draw_range = Global::BaseDrawRange; break; } case rendermode::shadows: { Config.draw_range = Global::BaseDrawRange * 0.5f; break; } + case rendermode::reflections: { Config.draw_range = Global::BaseDrawRange; break; } case rendermode::pickcontrols: { Config.draw_range = 50.f; break; } case rendermode::pickscenery: { Config.draw_range = Global::BaseDrawRange * 0.5f; break; } default: { Config.draw_range = 0.f; break; } @@ -541,7 +661,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f auto const lightvector = glm::normalize( glm::vec3{ Global::DayLight.direction.x, - std::min( Global::DayLight.direction.y, -0.15f ), + std::min( Global::DayLight.direction.y, -0.2f ), Global::DayLight.direction.z } ); // ...place the light source at the calculated centre and setup world space light view matrix... camera.position() = worldview.camera.position() + glm::dvec3{ frustumchunkcentre }; @@ -572,29 +692,40 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f frustumchunkmin.z - 500.f, frustumchunkmax.z + 500.f ); break; } + case rendermode::reflections: { + // projection + camera.projection() *= + glm::perspective( + glm::radians( 90.f ), + 1.f, + 0.1f * Global::ZoomFactor, + Config.draw_range * Global::fDistanceFactor ); + // modelview + camera.position() = ( + ( ( true == DebugCameraFlag ) && ( false == Ignoredebug ) ) ? + Global::DebugCameraPosition : + Global::pCameraPosition ); + glm::dvec3 const cubefacetargetvectors[ 6 ] = { { 1.0, 0.0, 0.0 }, { -1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 } }; + glm::dvec3 const cubefaceupvectors[ 6 ] = { { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 }, { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0 } }; + auto const cubefaceindex = m_environmentcubetextureface - GL_TEXTURE_CUBE_MAP_POSITIVE_X; + viewmatrix *= + glm::lookAt( + camera.position(), + camera.position() + cubefacetargetvectors[ cubefaceindex ], + cubefaceupvectors[ cubefaceindex ] ); + break; + } case rendermode::pickcontrols: case rendermode::pickscenery: { // TODO: scissor test for pick modes // projection - if( true == m_framebuffersupport ) { - auto const angle = Global::FieldOfView / Global::ZoomFactor; - auto const height = std::max( 1.0f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ); - camera.projection() *= - glm::perspective( - glm::radians( Global::FieldOfView / Global::ZoomFactor ), - std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ), - 0.1f * Global::ZoomFactor, - Config.draw_range * Global::fDistanceFactor ); - } - else { - camera.projection() *= - glm::perspective( - glm::radians( Global::FieldOfView / Global::ZoomFactor ), - std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.f, (float)Global::iWindowHeight ), - 0.1f * Global::ZoomFactor, - Config.draw_range * Global::fDistanceFactor ); - } + camera.projection() *= + glm::perspective( + glm::radians( Global::FieldOfView / Global::ZoomFactor ), + std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.f, (float)Global::iWindowHeight ), + 0.1f * Global::ZoomFactor, + Config.draw_range * Global::fDistanceFactor ); // modelview camera.position() = Global::pCameraPosition; World.Camera.SetMatrix( viewmatrix ); @@ -613,6 +744,17 @@ opengl_renderer::setup_matrices() { ::glMatrixMode( GL_PROJECTION ); OpenGLMatrices.load_matrix( m_renderpass.camera.projection() ); + + if( ( m_renderpass.draw_mode == rendermode::color ) + && ( m_environmentcubetexturesupport ) ) { + // special case, for colour render pass setup texture matrix for reflections cube map + Active_Texture( m_helpertextureunit ); + ::glMatrixMode( GL_TEXTURE ); + ::glPushMatrix(); + ::glMultMatrixf( glm::value_ptr( glm::inverse( glm::mat4{ glm::mat3{ m_renderpass.camera.modelview() } } ) ) ); + Active_Texture( m_diffusetextureunit ); + } + // trim modelview matrix just to rotation, since rendering is done in camera-centric world space ::glMatrixMode( GL_MODELVIEW ); OpenGLMatrices.load_matrix( glm::mat4( glm::mat3( m_renderpass.camera.modelview() ) ) ); @@ -632,7 +774,8 @@ opengl_renderer::setup_drawing( bool const Alpha ) { } switch( m_renderpass.draw_mode ) { - case rendermode::color: { + case rendermode::color: + case rendermode::reflections: { ::glEnable( GL_LIGHTING ); ::glShadeModel( GL_SMOOTH ); if( Global::iMultisampling ) { @@ -674,17 +817,35 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const // helper texture unit. // darkens previous stage, preparing data for the shadow texture unit to select from if( m_helpertextureunit >= 0 ) { + + Active_Texture( m_helpertextureunit ); + + if( ( true == Reflections ) + || ( ( true == Global::RenderShadows ) && ( true == Shadows ) ) ) { + // we need to have texture on the helper for either the reflection and shadow generation (or both) + if( true == m_environmentcubetexturesupport ) { + // bind dynamic environment cube if it's enabled... + // NOTE: environment cube map isn't part of regular texture system, so we use direct bind call here + ::glBindTexture( GL_TEXTURE_CUBE_MAP, m_environmentcubetexture ); + ::glEnable( GL_TEXTURE_CUBE_MAP ); + } + else { + // ...otherwise fallback on static spherical image + m_textures.bind( textureunit::helper, m_reflectiontexture ); + ::glEnable( GL_TEXTURE_2D ); + } + } + else { + if( true == m_environmentcubetexturesupport ) { + ::glDisable( GL_TEXTURE_CUBE_MAP ); + } + else { + ::glDisable( GL_TEXTURE_2D ); + } + } + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { - // setup reflection texture unit - ::glActiveTexture( m_helpertextureunit ); - Bind( m_reflectiontexture ); // TBD, TODO: move to reflection unit setup - ::glEnable( GL_TEXTURE_2D ); -/* - glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); - glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); - glEnable( GL_TEXTURE_GEN_S ); - glEnable( GL_TEXTURE_GEN_T ); -*/ + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( m_shadowcolor ) ); // TODO: dynamically calculated shadow colour, based on sun height @@ -698,14 +859,42 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); } else { - ::glActiveTexture( m_helpertextureunit ); - ::glDisable( GL_TEXTURE_2D ); -/* - ::glDisable( GL_TEXTURE_GEN_S ); - ::glDisable( GL_TEXTURE_GEN_T ); - ::glDisable( GL_TEXTURE_GEN_R ); - ::glDisable( GL_TEXTURE_GEN_Q ); -*/ + + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); // pass the previous stage colour down the chain + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + + if( true == Reflections ) { + if( true == m_environmentcubetexturesupport ) { + ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); + ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); + ::glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); + ::glEnable( GL_TEXTURE_GEN_S ); + ::glEnable( GL_TEXTURE_GEN_T ); + ::glEnable( GL_TEXTURE_GEN_R ); + } + else { + // fixed texture fall back + ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + ::glEnable( GL_TEXTURE_GEN_S ); + ::glEnable( GL_TEXTURE_GEN_T ); + } + } + else { + if( true == m_environmentcubetexturesupport ) { + ::glDisable( GL_TEXTURE_GEN_S ); + ::glDisable( GL_TEXTURE_GEN_T ); + ::glDisable( GL_TEXTURE_GEN_R ); + } + else { + ::glDisable( GL_TEXTURE_GEN_S ); + ::glDisable( GL_TEXTURE_GEN_T ); + } } } // shadow texture unit. @@ -715,7 +904,8 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const && ( true == Shadows ) && ( m_shadowcolor != colors::white ) ) { - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); + // NOTE: shadowmap isn't part of regular texture system, so we use direct bind call here ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); ::glEnable( GL_TEXTURE_2D ); // s @@ -745,7 +935,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const } else { // turn off shadow map tests - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); ::glDisable( GL_TEXTURE_2D ); ::glDisable( GL_TEXTURE_GEN_S ); @@ -754,9 +944,35 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::glDisable( GL_TEXTURE_GEN_Q ); } } + // reflections/normals texture unit + // NOTE: comes after diffuse stage in the operation chain + if( m_normaltextureunit >= 0 ) { + + Active_Texture( m_normaltextureunit ); + + if( true == Reflections ) { + ::glEnable( GL_TEXTURE_2D ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); // blend between object colour and env.reflection + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE ); // alpha channel of the normal map controls reflection strength + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + else { + ::glDisable( GL_TEXTURE_2D ); + } + } // diffuse texture unit. // NOTE: diffuse texture mapping is never fully disabled, alpha channel information is always included - ::glActiveTexture( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); + ::glEnable( GL_TEXTURE_2D ); if( true == Diffuse ) { // default behaviour, modulate with previous stage ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); @@ -781,6 +997,10 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); */ } + // update unit state + m_unitstate.diffuse = Diffuse; + m_unitstate.shadows = Shadows; + m_unitstate.reflections = Reflections; } // enables and disables specified texture units @@ -788,27 +1008,50 @@ void opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { // helper texture unit. if( m_helpertextureunit >= 0 ) { - if( ( true == Global::RenderShadows ) - && ( true == Shadows ) - && ( m_shadowcolor != colors::white ) ) { - ::glActiveTexture( m_helpertextureunit ); - ::glEnable( GL_TEXTURE_2D ); + + Active_Texture( m_helpertextureunit ); + if( ( true == Reflections ) + || ( ( true == Global::RenderShadows ) + && ( true == Shadows ) + && ( m_shadowcolor != colors::white ) ) ) { + if( true == m_environmentcubetexturesupport ) { + ::glEnable( GL_TEXTURE_CUBE_MAP ); + } + else { + ::glEnable( GL_TEXTURE_2D ); + } } else { - ::glActiveTexture( m_helpertextureunit ); - ::glDisable( GL_TEXTURE_2D ); + if( true == m_environmentcubetexturesupport ) { + ::glDisable( GL_TEXTURE_CUBE_MAP ); + } + else { + ::glDisable( GL_TEXTURE_2D ); + } } } // shadow texture unit. if( m_shadowtextureunit >= 0 ) { if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); ::glEnable( GL_TEXTURE_2D ); } else { - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } + } + // normal/reflection texture unit + if( m_normaltextureunit >= 0 ) { + if( true == Reflections ) { + + Active_Texture( m_normaltextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + Active_Texture( m_normaltextureunit ); ::glDisable( GL_TEXTURE_2D ); } } @@ -816,23 +1059,26 @@ opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool cons // NOTE: toggle actually disables diffuse texture mapping, unlike setup counterpart if( true == Diffuse ) { - ::glActiveTexture( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); ::glEnable( GL_TEXTURE_2D ); } else { - ::glActiveTexture( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); ::glDisable( GL_TEXTURE_2D ); } + // update unit state + m_unitstate.diffuse = Diffuse; + m_unitstate.shadows = Shadows; + m_unitstate.reflections = Reflections; } void opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { - ::glActiveTexture( m_helpertextureunit ); + Active_Texture( m_helpertextureunit ); ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( Shadowcolor ) ); // in-shadow colour multiplier - ::glActiveTexture( m_diffusetextureunit ); - + Active_Texture( m_diffusetextureunit ); } bool @@ -854,7 +1100,7 @@ opengl_renderer::Render( world_environment *Environment ) { return false; } - Bind( NULL ); + Bind_Material( null_handle ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_DEPTH_TEST ); ::glDepthMask( GL_FALSE ); @@ -903,7 +1149,7 @@ opengl_renderer::Render( world_environment *Environment ) { auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); // sun { - Bind( m_suntexture ); + Bind_Texture( m_suntexture ); ::glColor4f( suncolor.x, suncolor.y, suncolor.z, 1.0f ); auto const sunvector = Environment->m_sun.getDirection(); auto const sunposition = modelview * glm::vec4( sunvector.x, sunvector.y, sunvector.z, 1.0f ); @@ -924,7 +1170,7 @@ opengl_renderer::Render( world_environment *Environment ) { } // moon { - Bind( m_moontexture ); + Bind_Texture( m_moontexture ); glm::vec3 mooncolor( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ); ::glColor4f( mooncolor.x, mooncolor.y, mooncolor.z, static_cast( 1.0 - Global::fLuminance * 0.5 ) ); @@ -1032,21 +1278,50 @@ opengl_renderer::Vertices( geometry_handle const &Geometry ) const { return m_geometry.vertices( Geometry ); } -// texture methods -texture_handle -opengl_renderer::Fetch_Texture( std::string const &Filename, std::string const &Dir, int const Filter, bool const Loadnow ) { +// material methods +material_handle +opengl_renderer::Fetch_Material( std::string const &Filename, bool const Loadnow ) { - return m_textures.create( Filename, Dir, Filter, Loadnow ); + return m_materials.create( Filename, Loadnow ); } void -opengl_renderer::Bind( texture_handle const Texture ) { - // temporary until we separate the renderer - m_textures.bind( Texture ); +opengl_renderer::Bind_Material( material_handle const Material ) { + + auto const &material = m_materials.material( Material ); + if( false == Global::BasicRenderer ) { + m_textures.bind( textureunit::normals, material.texture2 ); + } + m_textures.bind( textureunit::diffuse, material.texture1 ); +} + +opengl_material const & +opengl_renderer::Material( material_handle const Material ) const { + + return m_materials.material( Material ); +} + +// texture methods +void +opengl_renderer::Active_Texture( GLint const Textureunit ) { + + return m_textures.unit( Textureunit ); +} + +texture_handle +opengl_renderer::Fetch_Texture( std::string const &Filename, bool const Loadnow ) { + + return m_textures.create( Filename, Loadnow ); +} + +void +opengl_renderer::Bind_Texture( texture_handle const Texture ) { + + m_textures.bind( textureunit::diffuse, Texture ); } opengl_texture const & -opengl_renderer::Texture( texture_handle const Texture ) { +opengl_renderer::Texture( texture_handle const Texture ) const { return m_textures.texture( Texture ); } @@ -1056,8 +1331,6 @@ opengl_renderer::Texture( texture_handle const Texture ) { bool opengl_renderer::Render( TGround *Ground ) { - ++TGroundRect::iFrameNumber; // zwięszenie licznika ramek (do usuwniania nadanimacji) - m_drawqueue.clear(); switch( m_renderpass.draw_mode ) { @@ -1120,6 +1393,19 @@ opengl_renderer::Render( TGround *Ground ) { } break; } + case rendermode::reflections: { + // reflections render only terrain geometry + for( int column = originx; column <= originx + segmentcount; ++column ) { + for( int row = originz; row <= originz + segmentcount; ++row ) { + + auto *cell = &Ground->Rects[ column ][ row ]; + if( m_renderpass.camera.visible( cell->m_area ) ) { + Render( cell ); + } + } + } + break; + } case rendermode::shadows: case rendermode::pickscenery: { // these render modes don't bother with anything non-visual, or lights @@ -1153,60 +1439,70 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { bool result { false }; // will be true if we do any rendering - if( Groundcell->iLastDisplay != Groundcell->iFrameNumber ) { - // tylko jezeli dany kwadrat nie był jeszcze renderowany - Groundcell->LoadNodes(); // ewentualne tworzenie siatek + Groundcell->LoadNodes(); // ewentualne tworzenie siatek - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // non-interactive scenery elements get neutral colour - ::glColor3fv( glm::value_ptr( colors::none ) ); - } - case rendermode::color: { - if( Groundcell->nRenderRect != nullptr ) { - // nieprzezroczyste trójkąty kwadratu kilometrowego - for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } + switch( m_renderpass.draw_mode ) { + case rendermode::pickscenery: { + // non-interactive scenery elements get neutral colour + ::glColor3fv( glm::value_ptr( colors::none ) ); + } + case rendermode::color: + case rendermode::reflections: { + if( Groundcell->nRenderRect != nullptr ) { + // nieprzezroczyste trójkąty kwadratu kilometrowego + for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { + Render( node ); } - break; + result = true; } - case rendermode::shadows: { - if( Groundcell->nRenderRect != nullptr ) { - // experimental, for shadows render both back and front faces, to supply back faces of the 'forest strips' - ::glDisable( GL_CULL_FACE ); - // nieprzezroczyste trójkąty kwadratu kilometrowego - for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - ::glEnable( GL_CULL_FACE ); + break; + } + case rendermode::shadows: { + if( Groundcell->nRenderRect != nullptr ) { + // experimental, for shadows render both back and front faces, to supply back faces of the 'forest strips' + ::glDisable( GL_CULL_FACE ); + // nieprzezroczyste trójkąty kwadratu kilometrowego + for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { + Render( node ); } - } - case rendermode::pickcontrols: - default: { - break; + result = true; + ::glEnable( GL_CULL_FACE ); } } + case rendermode::pickcontrols: + default: { + break; + } + } #ifdef EU07_USE_OLD_TERRAINCODE - if( Groundcell->nTerrain ) { + if( Groundcell->nTerrain ) { - Render( Groundcell->nTerrain ); - } + Render( Groundcell->nTerrain ); + } #endif - Groundcell->iLastDisplay = Groundcell->iFrameNumber; // drugi raz nie potrzeba - result = true; - // add the subcells of the cell to the draw queue - if( Groundcell->pSubRects != nullptr ) { - for( std::size_t subcellindex = 0; subcellindex < iNumSubRects * iNumSubRects; ++subcellindex ) { - auto subcell = Groundcell->pSubRects + subcellindex; - if( subcell->iNodeCount ) { - // o ile są jakieś obiekty, bo po co puste sektory przelatywać - m_drawqueue.emplace_back( - glm::length2( m_renderpass.camera.position() - glm::dvec3( subcell->m_area.center ) ), - subcell ); + // add the subcells of the cell to the draw queue + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::shadows: + case rendermode::pickscenery: { + if( Groundcell->pSubRects != nullptr ) { + for( std::size_t subcellindex = 0; subcellindex < iNumSubRects * iNumSubRects; ++subcellindex ) { + auto subcell = Groundcell->pSubRects + subcellindex; + if( subcell->iNodeCount ) { + // o ile są jakieś obiekty, bo po co puste sektory przelatywać + m_drawqueue.emplace_back( + glm::length2( m_renderpass.camera.position() - glm::dvec3( subcell->m_area.center ) ), + subcell ); + } } } + break; + } + case rendermode::reflections: + case rendermode::pickcontrols: + default: { + break; } } return result; @@ -1218,7 +1514,8 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { // oznaczanie aktywnych sektorów Groundsubcell->LoadNodes(); - Groundsubcell->RaAnimate(); // przeliczenia animacji torów w sektorze + // przeliczenia animacji torów w sektorze + Groundsubcell->RaAnimate( m_framestamp ); TGroundNode *node; @@ -1316,16 +1613,16 @@ opengl_renderer::Render( TGroundNode *Node ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } - if( ( distancesquared > ( Node->fSquareRadius * Global::fDistanceFactor ) ) - || ( distancesquared < ( Node->fSquareMinRadius / Global::fDistanceFactor ) ) ) { + if( ( distancesquared > Node->fSquareRadius ) + || ( distancesquared < Node->fSquareMinRadius ) ) { return false; } @@ -1367,38 +1664,22 @@ opengl_renderer::Render( TGroundNode *Node ) { break; } } - Node->Model->RaAnimate(); // jednorazowe przeliczenie animacji + Node->Model->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji Node->Model->RaPrepare(); if( Node->Model->pModel ) { // renderowanie rekurencyjne submodeli - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - Render( - Node->Model->pModel, - Node->Model->Material(), - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - SquareMagnitude( Node->pCenter - Global::pCameraPosition ), - Node->pCenter - m_renderpass.camera.position(), - Node->Model->vAngle ); - break; - } - default: { - auto const position = Node->pCenter - m_renderpass.camera.position(); - Render( - Node->Model->pModel, - Node->Model->Material(), - SquareMagnitude( position ), - position, - Node->Model->vAngle ); - break; - } - } + Render( + Node->Model->pModel, + Node->Model->Material(), + distancesquared, + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); } return true; } case GL_LINES: { - if( ( Node->Piece->geometry == NULL ) + if( ( Node->Piece->geometry == null_handle ) || ( Node->fLineThickness > 0.0 ) ) { return false; } @@ -1426,12 +1707,12 @@ opengl_renderer::Render( TGroundNode *Node ) { break; } } - auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 32.0 ); + auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); if( linewidth > 1.0 ) { ::glLineWidth( static_cast( linewidth ) ); } - GfxRenderer.Bind( 0 ); + GfxRenderer.Bind_Material( null_handle ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -1459,12 +1740,12 @@ opengl_renderer::Render( TGroundNode *Node ) { } case GL_TRIANGLES: { - if( ( Node->Piece->geometry == NULL ) + if( ( Node->Piece->geometry == null_handle ) || ( ( Node->iFlags & 0x10 ) == 0 ) ) { return false; } // setup - Bind( Node->TextureID ); + Bind_Material( Node->m_material ); switch( m_renderpass.draw_mode ) { case rendermode::color: { ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); @@ -1532,15 +1813,16 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { } // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji - auto const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - double squaredistance; + glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); + // lod visibility ranges are defined for base (x 1.0) viewing distance. for render we adjust them for actual range multiplier and zoom + float squaredistance; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { - squaredistance = SquareMagnitude( ( Dynamic->vPosition - Global::pCameraPosition ) / Global::ZoomFactor ); + squaredistance = glm::length2( glm::vec3{ glm::dvec3{ Dynamic->vPosition - Global::pCameraPosition } } / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + squaredistance = glm::length2( glm::vec3{ originoffset } / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -1566,8 +1848,7 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { if( Dynamic->InteriorLightLevel > 0.0f ) { // crude way to light the cabin, until we have something more complete in place - auto const cablight = Dynamic->InteriorLight * Dynamic->InteriorLightLevel; - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, &cablight.x ); + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Dynamic->InteriorLightLevel ) ); } Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); @@ -1659,8 +1940,7 @@ opengl_renderer::Render_cab( TDynamicObject *Dynamic ) { } if( Dynamic->InteriorLightLevel > 0.0f ) { // crude way to light the cabin, until we have something more complete in place - auto const cablight = Dynamic->InteriorLight * Dynamic->InteriorLightLevel; - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, &cablight.x ); + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Dynamic->InteriorLightLevel ) ); } // render Render( Dynamic->mdKabina, Dynamic->Material(), 0.0 ); @@ -1695,7 +1975,7 @@ opengl_renderer::Render_cab( TDynamicObject *Dynamic ) { } bool -opengl_renderer::Render( TModel3d *Model, material_data const *Material, double const Squaredistance ) { +opengl_renderer::Render( TModel3d *Model, material_data const *Material, float const Squaredistance ) { auto alpha = ( Material != nullptr ? @@ -1727,7 +2007,7 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, double } bool -opengl_renderer::Render( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { +opengl_renderer::Render( TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { ::glPushMatrix(); ::glTranslated( Position.x, Position.y, Position.z ); @@ -1749,8 +2029,8 @@ void opengl_renderer::Render( TSubModel *Submodel ) { if( ( Submodel->iVisible ) - && ( TSubModel::fSquareDist >= ( Submodel->fSquareMinDist / Global::fDistanceFactor ) ) - && ( TSubModel::fSquareDist <= ( Submodel->fSquareMaxDist * Global::fDistanceFactor ) ) ) { + && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) + && ( TSubModel::fSquareDist <= Submodel->fSquareMaxDist ) ) { if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); @@ -1765,7 +2045,8 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Submodel->iAlpha & Submodel->iFlags & 0x1F ) { // rysuj gdy element nieprzezroczysty switch( m_renderpass.draw_mode ) { - case rendermode::color: { + case rendermode::color: + case rendermode::reflections: { // NOTE: code disabled as normalization marking doesn't take into account scaling propagation down hierarchy chains // for the time being we'll do with enforced worst-case scaling method, when speculars are enabled #ifdef EU07_USE_OPTIMIZED_NORMALIZATION @@ -1784,12 +2065,12 @@ opengl_renderer::Render( TSubModel *Submodel ) { #endif // material configuration: // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // ...colors... ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub @@ -1799,11 +2080,15 @@ opengl_renderer::Render( TSubModel *Submodel ) { ::glEnable( GL_RESCALE_NORMAL ); } // ...luminance + auto const unitstate = m_unitstate; if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); // disable shadows so they don't obstruct self-lit items +/* setup_shadow_color( colors::white ); +*/ + switch_units( m_unitstate.diffuse, false, false ); } // main draw call @@ -1816,7 +2101,10 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); +/* setup_shadow_color( m_shadowcolor ); +*/ + switch_units( m_unitstate.diffuse, unitstate.shadows, unitstate.reflections ); } #ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { @@ -1839,12 +2127,12 @@ opengl_renderer::Render( TSubModel *Submodel ) { // scenery picking and shadow both use enforced colour and no frills // material configuration: // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // main draw call m_geometry.draw( Submodel->m_geometry ); @@ -1857,12 +2145,12 @@ opengl_renderer::Render( TSubModel *Submodel ) { m_pickcontrolsitems.emplace_back( Submodel ); ::glColor3fv( glm::value_ptr( pick_color( m_pickcontrolsitems.size() ) ) ); // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // main draw call m_geometry.draw( Submodel->m_geometry ); @@ -1879,7 +2167,8 @@ opengl_renderer::Render( TSubModel *Submodel ) { switch( m_renderpass.draw_mode ) { // spotlights are only rendered in colour mode(s) - case rendermode::color: { + case rendermode::color: + case rendermode::reflections: { auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); auto const lightcenter = modelview @@ -1896,13 +2185,13 @@ opengl_renderer::Render( TSubModel *Submodel ) { float const anglefactor = ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances - float const distancefactor = static_cast( std::max( 0.5, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / ( Submodel->fSquareMaxDist * Global::fDistanceFactor ) ) ); + float const distancefactor = std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ); if( lightlevel > 0.f ) { // material configuration: ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); - Bind( 0 ); + Bind_Material( null_handle ); ::glPointSize( std::max( 3.f, 5.f * distancefactor * anglefactor ) ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], lightlevel * anglefactor ); ::glDisable( GL_LIGHTING ); @@ -1911,15 +2200,22 @@ opengl_renderer::Render( TSubModel *Submodel ) { ::glPushMatrix(); ::glLoadIdentity(); ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // początek układu zostaje bez zmian - +/* setup_shadow_color( colors::white ); +*/ + auto const unitstate = m_unitstate; + switch_units( m_unitstate.diffuse, false, false ); // main draw call m_geometry.draw( Submodel->m_geometry ); // post-draw reset // re-enable shadows +/* setup_shadow_color( m_shadowcolor ); +*/ + switch_units( m_unitstate.diffuse, unitstate.shadows, unitstate.reflections ); + ::glPopMatrix(); ::glPopAttrib(); } @@ -1935,13 +2231,14 @@ opengl_renderer::Render( TSubModel *Submodel ) { switch( m_renderpass.draw_mode ) { // colour points are only rendered in colour mode(s) - case rendermode::color: { + case rendermode::color: + case rendermode::reflections: { if( Global::fLuminance < Submodel->fLight ) { // material configuration: ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT ); - Bind( 0 ); + Bind_Material( null_handle ); ::glDisable( GL_LIGHTING ); // main draw call @@ -1957,7 +2254,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { } } } - if( Submodel->Child != NULL ) + if( Submodel->Child != nullptr ) if( Submodel->iAlpha & Submodel->iFlags & 0x001F0000 ) Render( Submodel->Child ); @@ -1976,33 +2273,34 @@ opengl_renderer::Render( TSubModel *Submodel ) { void opengl_renderer::Render( TTrack *Track ) { - if( ( Track->TextureID1 == 0 ) - && ( Track->TextureID2 == 0 ) ) { + if( ( Track->m_material1 == 0 ) + && ( Track->m_material2 == 0 ) ) { return; } switch( m_renderpass.draw_mode ) { - case rendermode::color: { + case rendermode::color: + case rendermode::reflections: { Track->EnvironmentSet(); - if( Track->TextureID1 != 0 ) { - Bind( Track->TextureID1 ); + if( Track->m_material1 != 0 ) { + Bind_Material( Track->m_material1 ); m_geometry.draw( std::begin( Track->Geometry1 ), std::end( Track->Geometry1 ) ); } - if( Track->TextureID2 != 0 ) { - Bind( Track->TextureID2 ); + if( Track->m_material2 != 0 ) { + Bind_Material( Track->m_material2 ); m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); } Track->EnvironmentReset(); break; } - case rendermode::pickscenery: - case rendermode::shadows: { - if( Track->TextureID1 != 0 ) { - Bind( Track->TextureID1 ); + case rendermode::shadows: + case rendermode::pickscenery: { + if( Track->m_material1 != 0 ) { + Bind_Material( Track->m_material1 ); m_geometry.draw( std::begin( Track->Geometry1 ), std::end( Track->Geometry1 ) ); } - if( Track->TextureID2 != 0 ) { - Bind( Track->TextureID2 ); + if( Track->m_material2 != 0 ) { + Bind_Material( Track->m_material2 ); m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); } break; @@ -2032,11 +2330,12 @@ opengl_renderer::Render( TMemCell *Memcell ) { ::glPopAttrib(); break; } - case rendermode::pickscenery: - case rendermode::shadows: { + case rendermode::shadows: + case rendermode::pickscenery: { ::gluSphere( m_quadric, 0.35, 4, 2 ); break; } + case rendermode::reflections: case rendermode::pickcontrols: { break; } @@ -2105,16 +2404,16 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } - if( ( distancesquared > ( Node->fSquareRadius * Global::fDistanceFactor ) ) - || ( distancesquared < ( Node->fSquareMinRadius / Global::fDistanceFactor ) ) ) { + if( ( distancesquared > Node->fSquareRadius ) + || ( distancesquared < Node->fSquareMinRadius ) ) { return false; } @@ -2141,7 +2440,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { auto const color { Node->hvTraction->wire_color() }; ::glColor4f( color.r, color.g, color.b, linealpha ); - Bind( NULL ); + Bind_Material( null_handle ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -2169,34 +2468,18 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { Node->Model->RaPrepare(); if( Node->Model->pModel ) { // renderowanie rekurencyjne submodeli - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - Render_Alpha( - Node->Model->pModel, - Node->Model->Material(), - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - SquareMagnitude( Node->pCenter - Global::pCameraPosition ), - Node->pCenter - m_renderpass.camera.position(), - Node->Model->vAngle ); - break; - } - default: { - auto const position = Node->pCenter - m_renderpass.camera.position(); - Render_Alpha( - Node->Model->pModel, - Node->Model->Material(), - SquareMagnitude( position ), - position, - Node->Model->vAngle ); - break; - } - } + Render_Alpha( + Node->Model->pModel, + Node->Model->Material(), + distancesquared, + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); } return true; } case GL_LINES: { - if( ( Node->Piece->geometry == NULL ) + if( ( Node->Piece->geometry == null_handle ) || ( Node->fLineThickness < 0.0 ) ) { return false; } @@ -2212,12 +2495,12 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { glm::vec4( Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla std::min( 1.0, linealpha ) ) ) ); - auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 32.0 ); + auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); if( linewidth > 1.0 ) { ::glLineWidth( static_cast(linewidth) ); } - GfxRenderer.Bind( 0 ); + GfxRenderer.Bind_Material( null_handle ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -2235,14 +2518,14 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { } case GL_TRIANGLES: { - if( ( Node->Piece->geometry == NULL ) + if( ( Node->Piece->geometry == null_handle ) || ( ( Node->iFlags & 0x20 ) == 0 ) ) { return false; } // setup ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); - Bind( Node->TextureID ); + Bind_Material( Node->m_material ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -2270,15 +2553,16 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji - auto const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - double squaredistance; + glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); + // lod visibility ranges are defined for base (x 1.0) viewing distance. for render we adjust them for actual range multiplier and zoom + float squaredistance; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { - squaredistance = SquareMagnitude( ( Dynamic->vPosition - Global::pCameraPosition ) / Global::ZoomFactor ); + squaredistance = glm::length2( glm::vec3{ glm::dvec3{ Dynamic->vPosition - Global::pCameraPosition } } / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + squaredistance = glm::length2( glm::vec3{ originoffset } / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -2302,8 +2586,7 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { if( Dynamic->InteriorLightLevel > 0.0f ) { // crude way to light the cabin, until we have something more complete in place - auto const cablight = Dynamic->InteriorLight * Dynamic->InteriorLightLevel; - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, &cablight.x ); + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Dynamic->InteriorLightLevel ) ); } Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); @@ -2337,7 +2620,7 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { } bool -opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance ) { +opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance ) { auto alpha = ( Material != nullptr ? @@ -2369,7 +2652,7 @@ opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, d } bool -opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { +opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { ::glPushMatrix(); ::glTranslated( Position.x, Position.y, Position.z ); @@ -2391,8 +2674,8 @@ void opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // renderowanie przezroczystych przez DL if( ( Submodel->iVisible ) - && ( TSubModel::fSquareDist >= ( Submodel->fSquareMinDist / Global::fDistanceFactor ) ) - && ( TSubModel::fSquareDist <= ( Submodel->fSquareMaxDist * Global::fDistanceFactor ) ) ) { + && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) + && ( TSubModel::fSquareDist <= Submodel->fSquareMaxDist ) ) { if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); @@ -2423,12 +2706,12 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { } #endif // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // ...colors... ::glColor3fv( glm::value_ptr(Submodel->f4Diffuse) ); // McZapkie-240702: zamiast ub @@ -2436,11 +2719,15 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( Submodel->f4Specular * Global::DayLight.specular.a * m_speculartranslucentscalefactor ) ); } // ...luminance + auto const unitstate = m_unitstate; if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); // disable shadows so they don't obstruct self-lit items +/* setup_shadow_color( colors::white ); +*/ + switch_units( m_unitstate.diffuse, false, false ); } // main draw call @@ -2453,7 +2740,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); +/* setup_shadow_color( m_shadowcolor ); +*/ + switch_units( m_unitstate.diffuse, unitstate.shadows, unitstate.reflections ); } #ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { @@ -2495,7 +2785,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // setup ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT ); - Bind( m_glaretexture ); + Bind_Texture( m_glaretexture ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel ); ::glDisable( GL_LIGHTING ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); @@ -2505,7 +2795,11 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // początek układu zostaje bez zmian ::glRotated( std::atan2( lightcenter.x, lightcenter.z ) * 180.0 / M_PI, 0.0, 1.0, 0.0 ); // jedynie obracamy w pionie o kąt // disable shadows so they don't obstruct self-lit items +/* setup_shadow_color( colors::white ); +*/ + auto const unitstate = m_unitstate; + switch_units( m_unitstate.diffuse, false, false ); // main draw call m_geometry.draw( m_billboardgeometry ); @@ -2518,7 +2812,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // ...etc instead IF we had easy access to camera's forward and right vectors. TODO: check if Camera matrix is accessible */ // post-render cleanup +/* setup_shadow_color( m_shadowcolor ); +*/ + switch_units( m_unitstate.diffuse, unitstate.shadows, unitstate.reflections ); ::glPopMatrix(); ::glPopAttrib(); @@ -2527,7 +2824,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { } } - if( Submodel->Child != NULL ) { + if( Submodel->Child != nullptr ) { if( Submodel->eType == TP_TEXT ) { // tekst renderujemy w specjalny sposób, zamiast submodeli z łańcucha Child int i, j = (int)Submodel->pasText->size(); TSubModel *p; @@ -2560,7 +2857,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( Submodel->b_aAnim < at_SecondsJump ) Submodel->b_aAnim = at_None; // wyłączenie animacji dla kolejnego użycia submodelu - if( Submodel->Next != NULL ) + if( Submodel->Next != nullptr ) if( Submodel->iAlpha & Submodel->iFlags & 0x2F000000 ) Render_Alpha( Submodel->Next ); }; @@ -2585,9 +2882,8 @@ opengl_renderer::Update_Pick_Control() { if( true == m_framebuffersupport ) { // ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); pickbufferpos = glm::ivec2{ - mousepos.x * EU07_PICKBUFFERSIZE / Global::iWindowWidth, - mousepos.y * EU07_PICKBUFFERSIZE / Global::iWindowHeight - }; + mousepos.x * EU07_PICKBUFFERSIZE / std::max( 1, Global::iWindowWidth ), + mousepos.y * EU07_PICKBUFFERSIZE / std::max( 1, Global::iWindowHeight ) }; } else { // ::glReadBuffer( GL_BACK ); @@ -2677,7 +2973,7 @@ opengl_renderer::Update( double const Deltatime ) { m_framerate = 1000.f / ( m_drawtime / 20.f ); // adjust draw ranges etc, based on recent performance - auto const framerate = 1000.0f / (m_drawtimecolorpass / 20.0f); + auto const framerate = 1000.f / (m_drawtimecolorpass / 20.f); float targetfactor; if( framerate > 90.0 ) { targetfactor = 3.0f; } @@ -2863,17 +3159,29 @@ opengl_renderer::Init_caps() { Global::DynamicLightCount = std::min( Global::DynamicLightCount, maxlights - 1 ); WriteLog( "Dynamic light amount capped at " + std::to_string( Global::DynamicLightCount ) + " (" + std::to_string(maxlights) + " lights total supported by the gfx card)" ); } - { + // select renderer mode + if( true == Global::BasicRenderer ) { + WriteLog( "Basic renderer selected, shadow and reflection mapping will be disabled" ); + Global::RenderShadows = false; + m_diffusetextureunit = GL_TEXTURE0; + m_helpertextureunit = -1; + m_shadowtextureunit = -1; + m_normaltextureunit = -1; + } + else { GLint maxtextureunits; ::glGetIntegerv( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits ); if( maxtextureunits < 4 ) { WriteLog( "Less than 4 texture units, shadow and reflection mapping will be disabled" ); + Global::BasicRenderer = true; Global::RenderShadows = false; m_diffusetextureunit = GL_TEXTURE0; - m_shadowtextureunit = -1; m_helpertextureunit = -1; + m_shadowtextureunit = -1; + m_normaltextureunit = -1; } } + if( Global::iMultisampling ) { WriteLog( "Using multisampling x" + std::to_string( 1 << Global::iMultisampling ) ); } diff --git a/renderer.h b/renderer.h index 30e5dcad..067f64c0 100644 --- a/renderer.h +++ b/renderer.h @@ -11,7 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "openglgeometrybank.h" -#include "texture.h" +#include "material.h" #include "lightarray.h" #include "dumb3d.h" #include "frustum.h" @@ -73,13 +73,6 @@ struct opengl_technique { }; -// a collection of parameters for the rendering setup. -// for modern opengl this translates to set of attributes for the active shaders, -// for legacy opengl this is basically just texture(s) assigned to geometry -struct opengl_material { - -}; - // simple camera object. paired with 'virtual camera' in the scene class opengl_camera { @@ -175,13 +168,22 @@ public: // provides direct access to vertex data of specfied chunk vertex_array const & Vertices( geometry_handle const &Geometry ) const; - // texture methods - texture_handle - Fetch_Texture( std::string const &Filename, std::string const &Dir = szTexturePath, int const Filter = -1, bool const Loadnow = true ); + // material methods + material_handle + Fetch_Material( std::string const &Filename, bool const Loadnow = true ); void - Bind( texture_handle const Texture ); + Bind_Material( material_handle const Material ); + opengl_material const & + Material( material_handle const Material ) const; + // texture methods + void + Active_Texture( GLint const Textureunit ); + texture_handle + Fetch_Texture( std::string const &Filename, bool const Loadnow = true ); + void + Bind_Texture( texture_handle const Texture ); opengl_texture const & - Texture( texture_handle const Texture ); + Texture( texture_handle const Texture ) const; // light methods void Disable_Lights(); @@ -211,10 +213,18 @@ private: none, color, shadows, + reflections, pickcontrols, pickscenery }; + enum textureunit { + helper = 0, + shadows, + normals, + diffuse + }; + typedef std::pair< double, TSubRect * > distancesubcell_pair; struct renderpass_config { @@ -224,14 +234,18 @@ private: float draw_range { 0.0f }; }; + struct units_state { + + bool diffuse { false }; + bool shadows { false }; + bool reflections { false }; + }; + typedef std::vector opengllight_array; // methods bool Init_caps(); - // runs jobs needed to generate graphics for specified render pass - void - Render_pass( rendermode const Mode ); void setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false ); void @@ -244,6 +258,12 @@ private: setup_shadow_color( glm::vec4 const &Shadowcolor ); void switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ); + // runs jobs needed to generate graphics for specified render pass + void + Render_pass( rendermode const Mode ); + // creates dynamic environment cubemap + bool + Render_reflections(); bool Render( world_environment *Environment ); bool @@ -257,9 +277,9 @@ private: bool Render( TDynamicObject *Dynamic ); bool - Render( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + Render( TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); bool - Render( TModel3d *Model, material_data const *Material, double const Squaredistance ); + Render( TModel3d *Model, material_data const *Material, float const Squaredistance ); void Render( TSubModel *Submodel ); void @@ -277,9 +297,9 @@ private: bool Render_Alpha( TDynamicObject *Dynamic ); bool - Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); bool - Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance ); + Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance ); void Render_Alpha( TSubModel *Submodel ); void @@ -292,34 +312,45 @@ private: // members GLFWwindow *m_window { nullptr }; geometrybank_manager m_geometry; + material_manager m_materials; texture_manager m_textures; opengllight_array m_lights; - geometry_handle m_billboardgeometry { NULL, NULL }; + geometry_handle m_billboardgeometry { 0, 0 }; texture_handle m_glaretexture { -1 }; texture_handle m_suntexture { -1 }; texture_handle m_moontexture { -1 }; texture_handle m_reflectiontexture { -1 }; GLUquadricObj *m_quadric { nullptr }; // helper object for drawing debug mode scene elements - + // TODO: refactor framebuffer stuff into an object bool m_framebuffersupport { false }; #ifdef EU07_USE_PICKING_FRAMEBUFFER - GLuint m_pickframebuffer { NULL }; // TODO: refactor pick framebuffer stuff into an object - GLuint m_picktexture { NULL }; - GLuint m_pickdepthbuffer { NULL }; + GLuint m_pickframebuffer { 0 }; + GLuint m_picktexture { 0 }; + GLuint m_pickdepthbuffer { 0 }; #endif - int m_shadowbuffersize { 4096 }; - GLuint m_shadowframebuffer { NULL }; - GLuint m_shadowtexture { NULL }; + int m_shadowbuffersize { 2048 }; + GLuint m_shadowframebuffer { 0 }; + GLuint m_shadowtexture { 0 }; #ifdef EU07_USE_DEBUG_SHADOWMAP - GLuint m_shadowdebugtexture{ NULL }; + GLuint m_shadowdebugtexture{ 0 }; #endif glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space + GLuint m_environmentframebuffer { 0 }; + GLuint m_environmentcubetexture { 0 }; + GLuint m_environmentdepthbuffer { 0 }; + bool m_environmentcubetexturesupport { false }; // indicates whether we can use the dynamic environment cube map + int m_environmentcubetextureface { 0 }; // helper, currently processed cube map face + int m_environmentupdatetime { 0 }; // time of the most recent environment map update + glm::dvec3 m_environmentupdatelocation; // coordinates of most recent environment map update - int m_shadowtextureunit { GL_TEXTURE1 }; int m_helpertextureunit { GL_TEXTURE0 }; - int m_diffusetextureunit { GL_TEXTURE2 }; + int m_shadowtextureunit { GL_TEXTURE1 }; + int m_normaltextureunit { GL_TEXTURE2 }; + int m_diffusetextureunit{ GL_TEXTURE3 }; + units_state m_unitstate; + unsigned int m_framestamp; // id of currently rendered gfx frame float m_drawtime { 1000.f / 30.f * 20.f }; // start with presumed 'neutral' average of 30 fps std::chrono::steady_clock::time_point m_drawstart; // cached start time of previous frame float m_framerate; @@ -327,6 +358,7 @@ private: float m_drawtimeshadowpass { 0.f }; double m_updateaccumulator { 0.0 }; std::string m_debuginfo; + std::string m_pickdebuginfo; glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f }; glm::vec4 m_shadowcolor { 0.5f, 0.5f, 0.5f, 1.f }; diff --git a/sky.cpp b/sky.cpp index 86aba2fe..7d6ee679 100644 --- a/sky.cpp +++ b/sky.cpp @@ -24,25 +24,4 @@ void TSky::Init() { } }; -#ifdef EU07_USE_OLD_RENDERCODE -void TSky::Render( glm::vec3 const &Tint ) -{ - if (mdCloud) - { // jeśli jest model nieba - // setup - ::glEnable( GL_LIGHTING ); - GfxRenderer.Disable_Lights(); - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr(Tint) ); - // render - GfxRenderer.Render( mdCloud, nullptr, 100.0 ); - GfxRenderer.Render_Alpha( mdCloud, nullptr, 100.0 ); - // post-render cleanup - GLfloat noambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, noambient ); - ::glEnable( GL_LIGHT0 ); // other lights will be enabled during lights update - ::glDisable( GL_LIGHTING ); - } -}; -#endif - //--------------------------------------------------------------------------- diff --git a/sky.h b/sky.h index 4b544e0e..ca48434e 100644 --- a/sky.h +++ b/sky.h @@ -21,9 +21,6 @@ private: public: void Init(); -#ifdef EU07_USE_OLD_RENDERCODE - void Render( glm::vec3 const &Tint = glm::vec3(1.0f, 1.0f, 1.0f) ); -#endif }; //--------------------------------------------------------------------------- diff --git a/skydome.cpp b/skydome.cpp index 7d136971..342a8e00 100644 --- a/skydome.cpp +++ b/skydome.cpp @@ -122,11 +122,11 @@ void CSkyDome::Render() { // build the buffers ::glGenBuffers( 1, &m_vertexbuffer ); ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - ::glBufferData( GL_ARRAY_BUFFER, m_vertices.size() * sizeof( float3 ), m_vertices.data(), GL_STATIC_DRAW ); + ::glBufferData( GL_ARRAY_BUFFER, m_vertices.size() * sizeof( glm::vec3 ), m_vertices.data(), GL_STATIC_DRAW ); ::glGenBuffers( 1, &m_coloursbuffer ); ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); - ::glBufferData( GL_ARRAY_BUFFER, m_colours.size() * sizeof( float3 ), m_colours.data(), GL_DYNAMIC_DRAW ); + ::glBufferData( GL_ARRAY_BUFFER, m_colours.size() * sizeof( glm::vec3 ), m_colours.data(), GL_DYNAMIC_DRAW ); ::glGenBuffers( 1, &m_indexbuffer ); ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); @@ -138,10 +138,10 @@ void CSkyDome::Render() { ::glEnableClientState( GL_COLOR_ARRAY ); // positions ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - ::glVertexPointer( 3, GL_FLOAT, sizeof( float3 ), reinterpret_cast( 0 ) ); + ::glVertexPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast( 0 ) ); // colours ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); - ::glColorPointer( 3, GL_FLOAT, sizeof( float3 ), reinterpret_cast( 0 ) ); + ::glColorPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast( 0 ) ); // indices ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); ::glDrawElements( GL_TRIANGLES, static_cast( m_indices.size() ), GL_UNSIGNED_SHORT, reinterpret_cast( 0 ) ); @@ -345,7 +345,7 @@ void CSkyDome::RebuildColors() { if( m_coloursbuffer != -1 ) { // the colour buffer was already initialized, so on this run we update its content ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); - ::glBufferSubData( GL_ARRAY_BUFFER, 0, m_colours.size() * sizeof( float3 ), m_colours.data() ); + ::glBufferSubData( GL_ARRAY_BUFFER, 0, m_colours.size() * sizeof( glm::vec3 ), m_colours.data() ); } } diff --git a/stars.cpp b/stars.cpp index ebbd84a6..b7c9ef79 100644 --- a/stars.cpp +++ b/stars.cpp @@ -12,21 +12,3 @@ cStars::init() { m_stars = TModelsManager::GetModel( "models\\skydome_stars.t3d", false ); } -#ifdef EU07_USE_OLD_RENDERCODE -void -cStars::render() { - // setup - ::glPushMatrix(); - - ::glRotatef( m_latitude, 1.0f, 0.0f, 0.0f ); // ustawienie osi OY na północ - ::glRotatef( -std::fmod( (float)Global::fTimeAngleDeg, 360.0f ), 0.0f, 1.0f, 0.0f ); // obrót dobowy osi OX - - ::glPointSize( 2.0f ); - // render - GfxRenderer.Render( &m_stars, nullptr, 1.0 ); - // post-render cleanup - ::glPointSize( 3.0f ); - - ::glPopMatrix(); -} -#endif diff --git a/stars.h b/stars.h index dc579ff5..7541bf77 100644 --- a/stars.h +++ b/stars.h @@ -15,9 +15,6 @@ public: // methods: void init(); -#ifdef EU07_USE_OLD_RENDERCODE - void render(); -#endif // constructors: // deconstructor: diff --git a/stdafx.h b/stdafx.h index ca6107c9..c9a7f419 100644 --- a/stdafx.h +++ b/stdafx.h @@ -29,6 +29,7 @@ #include #endif // stl +#include #include #include #define _USE_MATH_DEFINES diff --git a/sun.cpp b/sun.cpp index ed08aa2b..9f4ac2b5 100644 --- a/sun.cpp +++ b/sun.cpp @@ -34,8 +34,8 @@ cSun::update() { move(); glm::vec3 position( 0.f, 0.f, -2000.f * Global::fDistanceFactor ); - position = glm::rotateX( position, glm::radians( m_body.elevref ) ); - position = glm::rotateY( position, glm::radians( -m_body.hrang ) ); + position = glm::rotateX( position, glm::radians( static_cast( m_body.elevref ) ) ); + position = glm::rotateY( position, glm::radians( static_cast( -m_body.hrang ) ) ); m_position = position; } @@ -43,24 +43,17 @@ cSun::update() { void cSun::render() { -/* - glLightfv(GL_LIGHT0, GL_POSITION, position.getVector() ); // sun - - GLfloat LightPosition[]= { 10.0f, 50.0f, -5.0f, 1.0f }; // ambient - glLightfv(GL_LIGHT1, GL_POSITION, LightPosition ); -*/ - glColor4f( 255.0f/255.0f, 242.0f/255.0f, 231.0f/255.0f, 1.f ); + ::glColor4f( 255.f / 255.f, 242.f / 255.f, 231.f / 255.f, 1.f ); // debug line to locate the sun easier - Math3D::vector3 position = m_position; - glBegin( GL_LINES ); - glVertex3f( position.x, position.y, position.z ); - glVertex3f( position.x, 0.0f, position.z ); - glEnd(); - glPushMatrix(); - glTranslatef( position.x, position.y, position.z ); + ::glBegin( GL_LINES ); + ::glVertex3fv( glm::value_ptr( m_position ) ); + ::glVertex3f( m_position.x, 0.f, m_position.z ); + ::glEnd(); + ::glPushMatrix(); + ::glTranslatef( m_position.x, m_position.y, m_position.z ); // radius is a result of scaling true distance down to 2km -- it's scaled by equal ratio - gluSphere( sunsphere, (float)(m_body.distance * 9.359157), 12, 12 ); - glPopMatrix(); + ::gluSphere( sunsphere, m_body.distance * 9.359157, 12, 12 ); + ::glPopMatrix(); } glm::vec3 diff --git a/translation.h b/translation.h index c18a865b..297f35cf 100644 --- a/translation.h +++ b/translation.h @@ -24,6 +24,7 @@ static std::unordered_map m_cabcontrols = { { "brakectrl:", "train brake" }, { "localbrake:", "independent brake" }, { "manualbrake:", "manual brake" }, + { "alarmchain:", "emergency brake" }, { "brakeprofile_sw:", "brake acting speed" }, { "brakeprofileg_sw:", "brake acting speed: cargo" }, { "brakeprofiler_sw:", "brake acting speed: rapid" }, diff --git a/uilayer.cpp b/uilayer.cpp index 4680a728..cf1f0fbc 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -100,9 +100,9 @@ ui_layer::set_background( std::string const &Filename ) { m_background = GfxRenderer.Fetch_Texture( Filename ); } else { - m_background = NULL; + m_background = null_handle; } - if( m_background != NULL ) { + if( m_background != null_handle ) { auto const &texture = GfxRenderer.Texture( m_background ); m_progressbottom = ( texture.width() != texture.height() ); } @@ -216,7 +216,7 @@ ui_layer::render_background() { if( m_background == 0 ) return; // NOTE: we limit/expect the background to come with 4:3 ratio. // TODO, TBD: if we expose texture width or ratio from texture object, this limitation could be lifted - GfxRenderer.Bind( m_background ); + GfxRenderer.Bind_Texture( m_background ); auto const height { 768.0f }; auto const &texture = GfxRenderer.Texture( m_background ); float const width = ( @@ -235,11 +235,11 @@ ui_layer::render_background() { void ui_layer::render_texture() { - if( m_texture != NULL ) { + if( m_texture != 0 ) { ::glColor4f( 1.f, 1.f, 1.f, 1.f ); ::glDisable( GL_BLEND ); - GfxRenderer.Bind( NULL ); + GfxRenderer.Bind_Texture( null_handle ); ::glBindTexture( GL_TEXTURE_2D, m_texture ); auto const size = 512.f; @@ -254,6 +254,8 @@ ui_layer::render_texture() { glEnd(); + ::glBindTexture( GL_TEXTURE_2D, 0 ); + ::glEnable( GL_BLEND ); } } diff --git a/uilayer.h b/uilayer.h index 12800599..815ea703 100644 --- a/uilayer.h +++ b/uilayer.h @@ -55,7 +55,7 @@ public: void set_background( std::string const &Filename = "" ); void - set_texture( GLuint Texture = NULL ) { m_texture = Texture; } + set_texture( GLuint Texture = 0 ) { m_texture = Texture; } void set_tooltip( std::string const &Tooltip ) { m_tooltip = Tooltip; } void @@ -98,8 +98,8 @@ private: std::string m_progresstext; // label placed over the progress bar bool m_progressbottom { false }; // location of the progress bar - texture_handle m_background { NULL }; // path to texture used as the background. size depends on mAspect. - GLuint m_texture { NULL }; + texture_handle m_background { null_handle }; // path to texture used as the background. size depends on mAspect. + GLuint m_texture { 0 }; std::vector > m_panels; std::string m_tooltip; }; diff --git a/version.h b/version.h index a572b462..7043a400 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 805 +#define VERSION_MINOR 902 #define VERSION_REVISION 0