From b53ac3bf109afc05fa181f7b34743026fd635dcf Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 3 Jan 2019 20:40:45 +0100 Subject: [PATCH 1/3] build 190103. configurable reflections refresh rate, resolution based point light size, ai master controller logic enhancement, minor event diagnostic enhancement, minor bug fixes --- Driver.cpp | 19 +++++++---- Event.cpp | 2 +- Globals.cpp | 8 +++++ Globals.h | 1 + McZapkie/MOVER.h | 7 ---- McZapkie/Mover.cpp | 79 ++++++++++------------------------------------ Train.cpp | 8 +++-- renderer.cpp | 15 ++++++--- translation.cpp | 2 +- version.h | 4 +-- 10 files changed, 59 insertions(+), 86 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 38a01eb0..b4236a09 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2885,10 +2885,12 @@ bool TController::IncSpeed() if( usefieldshunting ) { // to dać bocznik // engage the shuntfield only if there's sufficient power margin to draw from + auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 50.0 ) }; + OK = ( - fVoltage > useseriesmodevoltage + 0.0125 * mvControlling->EnginePowerSource.CollectorParameters.MaxV ? + sufficientpowermargin ? mvControlling->IncScndCtrl( 1 ) : - false ); + true ); } else { // jeśli ustawiony bocznik to bocznik na zero po chamsku @@ -2896,10 +2898,12 @@ bool TController::IncSpeed() mvControlling->DecScndCtrl( 2 ); } // kręcimy nastawnik jazdy + auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 80.0 : 40.0 ) }; + OK = ( - mvControlling->DelayCtrlFlag ? - true : - mvControlling->IncMainCtrl( 1 ) ); + ( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ? + mvControlling->IncMainCtrl( 1 ) : + true ); // czekaj na 1 pozycji, zanim się nie włączą liniowe if( true == mvControlling->StLinFlag ) { iDrivigFlags |= moveIncSpeed; @@ -4008,7 +4012,10 @@ TController::UpdateSituation(double dt) { } } } - if( fVoltage < 0.75 * mvControlling->EnginePowerSource.CollectorParameters.MaxV ) { + + auto const useseriesmodevoltage { mvControlling->EnginePowerSource.CollectorParameters.MaxV * ( IsHeavyCargoTrain ? 0.70 : 0.80 ) }; + + if( fVoltage <= useseriesmodevoltage ) { // if the power station is heavily burdened try to reduce the load switch( mvControlling->EngineType ) { diff --git a/Event.cpp b/Event.cpp index 80c116fd..176b5cbc 100644 --- a/Event.cpp +++ b/Event.cpp @@ -97,7 +97,7 @@ basic_event::event_conditions::test() const { match_value_2, flags ); - std::string comparisonlog = "Test: MemCompare - "; + std::string comparisonlog = "Test: MemCompare - " + cell->name() + " - "; comparisonlog += "[" + cell->Text() + "]" diff --git a/Globals.cpp b/Globals.cpp index 843acef8..41ebbc35 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -384,6 +384,14 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(); Parser >> ResourceMove; } + else if( token == "gfx.reflections.framerate" ) { + + auto const updatespersecond { std::abs( Parser.getToken() ) }; + ReflectionUpdatesPerSecond = ( + updatespersecond > 0 ? + 1000 / std::min( 30.0, updatespersecond ) : + 0 ); + } else if (token == "timespeed") { // przyspieszenie czasu, zmienna do testów diff --git a/Globals.h b/Globals.h index db2e2484..bfe3a2a6 100644 --- a/Globals.h +++ b/Globals.h @@ -115,6 +115,7 @@ struct global_settings { float depth{ 250.f }; float distance{ 500.f }; // no longer used } shadowtune; + int ReflectionUpdatesPerSecond{ static_cast( 1000 / ( 1.0 / 300.0 ) ) }; bool bUseVBO{ true }; // czy jest VBO w karcie graficznej (czy użyć) float AnisotropicFiltering{ 8.f }; // requested level of anisotropic filtering. TODO: move it to renderer object float FieldOfView{ 45.f }; // vertical field of view for the camera. TODO: move it to the renderer diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 22c0dc92..63f03b76 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1003,13 +1003,6 @@ public: TRotation Rot { 0.0, 0.0, 0.0 }; std::string Name; /*nazwa wlasna*/ TCoupling Couplers[2]; //urzadzenia zderzno-sprzegowe, polaczenia miedzy wagonami -#ifdef EU07_USE_OLD_HVCOUPLERS - double HVCouplers[ 2 ][ 2 ]; //przewod WN - enum hvcoupler { - current = 0, - voltage - }; -#endif bool EventFlag = false; /*!o true jesli cos nietypowego sie wydarzy*/ int SoundFlag = 0; /*!o patrz stale sound_ */ double DistCounter = 0.0; /*! licznik kilometrow */ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b5ef617d..d0918c15 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -316,12 +316,6 @@ ActiveCab( Cab ) Couplers[b].DmaxC = 0.1; Couplers[b].FmaxC = 1000.0; } -#ifdef EU07_USE_OLD_HVCOUPLERS - for( int side = 0; side < 2; ++side ) { - HVCouplers[ side ][ hvcoupler::current ] = 0.0; - HVCouplers[ side ][ hvcoupler::voltage ] = 0.0; - } -#endif for( int b = 0; b < 3; ++b ) { BrakeCylMult[ b ] = 0.0; } @@ -816,13 +810,20 @@ void TMoverParameters::UpdateBatteryVoltage(double dt) && ( EngineType != TEngineType::WheelsDriven ) && ( NominalBatteryVoltage > 0 ) ) { + // HACK: allow to draw power also from adjacent converter, applicable for EMUs + // TODO: expand power cables system to include low voltage power transfers + auto const converteractive{ ( + ( ConverterFlag ) + || ( ( ( Couplers[ side::front ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::front ].Connected->ConverterFlag ) + || ( ( ( Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::rear ].Connected->ConverterFlag ) ) }; + if ((NominalBatteryVoltage / BatteryVoltage < 1.22) && Battery) { // 110V - if (!ConverterFlag) + if (!converteractive) sn1 = (dt * 2.0); // szybki spadek do ok 90V else sn1 = 0; - if (ConverterFlag) + if (converteractive) sn2 = -(dt * 2.0); // szybki wzrost do 110V else sn2 = 0; @@ -846,7 +847,7 @@ void TMoverParameters::UpdateBatteryVoltage(double dt) sn1 = (dt * 0.0046); else sn1 = 0; - if (ConverterFlag) + if (converteractive) sn2 = -(dt * 50); // szybki wzrost do 110V else sn2 = 0; @@ -1120,33 +1121,18 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap if( ( Couplers[ side ].CouplingFlag & ctrain_power ) || ( ( Heating ) && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { -#ifdef EU07_USE_OLD_HVCOUPLERS - HVCouplers[ oppositeside ][ hvcoupler::voltage ] = - std::max( - std::abs( hvc ), - Couplers[ side ].Connected->HVCouplers[ Couplers[ side ].ConnectedNr ][ hvcoupler::voltage ] - HVCouplers[ side ][ hvcoupler::current ] * 0.02 ); -#else auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ Couplers[ side ].ConnectedNr ]; Couplers[ oppositeside ].power_high.voltage = std::max( std::abs( hvc ), connectedcoupler.power_high.voltage - Couplers[ side ].power_high.current * 0.02 ); -#endif } else { -#ifdef EU07_USE_OLD_HVCOUPLERS - HVCouplers[ oppositeside ][ hvcoupler::voltage ] = std::abs( hvc ) - HVCouplers[ side ][ hvcoupler::current ] * 0.02; -#else Couplers[ oppositeside ].power_high.voltage = std::abs( hvc ) - Couplers[ side ].power_high.current * 0.02; -#endif } } -#ifdef EU07_USE_OLD_HVCOUPLERS - hvc = HVCouplers[ side::front ][ hvcoupler::voltage ] + HVCouplers[ side::rear ][ hvcoupler::voltage ]; -#else hvc = Couplers[ side::front ].power_high.voltage + Couplers[ side::rear ].power_high.voltage; -#endif if( std::abs( PantFrontVolt ) + std::abs( PantRearVolt ) < 1.0 ) { // bez napiecia... @@ -1160,29 +1146,17 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap if( ( Couplers[ side ].CouplingFlag & ctrain_power ) || ( ( Heating ) && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { -#ifdef EU07_USE_OLD_HVCOUPLERS - auto const oppositeside = ( Couplers[side].ConnectedNr == side::front ? side::rear : side::front ); - HVCouplers[ side ][ hvcoupler::current ] = - Couplers[side].Connected->HVCouplers[oppositeside][hvcoupler::current] + - Itot * HVCouplers[side][hvcoupler::voltage] / hvc; // obciążenie rozkladane stosownie do napiec -#else - auto const &connectedsothercoupler = + auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == side::front ? side::rear : side::front ) ]; Couplers[ side ].power_high.current = - connectedsothercoupler.power_high.current + connectedcoupler.power_high.current + Itot * Couplers[ side ].power_high.voltage / hvc; // obciążenie rozkladane stosownie do napiec -#endif } else { -#ifdef EU07_USE_OLD_HVCOUPLERS - // pierwszy pojazd - HVCouplers[side][hvcoupler::current] = Itot * HVCouplers[side][hvcoupler::voltage] / hvc; -#else Couplers[ side ].power_high.current = Itot * Couplers[ side ].power_high.voltage / hvc; -#endif } } } @@ -1196,19 +1170,13 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap if( ( Couplers[ side ].CouplingFlag & ctrain_power ) || ( ( Heating ) && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { -#ifdef EU07_USE_OLD_HVCOUPLERS - auto const oppositeside = ( Couplers[ side ].ConnectedNr == side::front ? side::rear : side::front ); - TotalCurrent += Couplers[ side ].Connected->HVCouplers[ oppositeside ][ hvcoupler::current ]; - HVCouplers[ side ][ hvcoupler::current ] = 0.0; -#else - auto const &connectedsothercoupler = + auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == side::front ? side::rear : side::front ) ]; - TotalCurrent += connectedsothercoupler.power_high.current; + TotalCurrent += connectedcoupler.power_high.current; Couplers[ side ].power_high.current = 0.0; -#endif } } } @@ -4007,11 +3975,9 @@ void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) Voltage = std::max( RunningTraction.TractionVoltage, -#ifdef EU07_USE_OLD_HVCOUPLERS - std::max( HVCouplers[side::front][hvcoupler::voltage], HVCouplers[side::rear][hvcoupler::voltage] ) ); -#else - std::max( Couplers[ side::front ].power_high.voltage, Couplers[ side::rear ].power_high.voltage ) ); -#endif + std::max( + Couplers[ side::front ].power_high.voltage, + Couplers[ side::rear ].power_high.voltage ) ); } else { Voltage = 0; @@ -7023,16 +6989,6 @@ std::string TMoverParameters::EngineDescription(int what) const // ************************************************************************************************* double TMoverParameters::GetTrainsetVoltage(void) {//ABu: funkcja zwracajaca napiecie dla calego skladu, przydatna dla EZT -#ifdef EU07_USE_OLD_HVCOUPLERS - return std::max( - HVCouplers[ side::front ][ hvcoupler::voltage ], - HVCouplers[ side::rear ][ hvcoupler::voltage ] ); -#else -/* - return std::max( - Couplers[ side::front ].power_high.voltage, - Couplers[ side::rear ].power_high.voltage ); -*/ return std::max( ( ( ( Couplers[side::front].Connected ) && ( ( Couplers[ side::front ].CouplingFlag & ctrain_power ) @@ -7046,7 +7002,6 @@ double TMoverParameters::GetTrainsetVoltage(void) && ( Couplers[ side::rear ].CouplingFlag & ctrain_heating ) ) ) ) ? Couplers[ side::rear ].Connected->Couplers[ Couplers[ side::rear ].ConnectedNr ].power_high.voltage : 0.0 ) ); -#endif } // ************************************************************************************************* diff --git a/Train.cpp b/Train.cpp index 2da16b35..3fe0e1d4 100644 --- a/Train.cpp +++ b/Train.cpp @@ -550,7 +550,7 @@ PyObject *TTrain::GetTrainState() { PyDict_SetItemString( dict, "train_enginetype", PyGetString( timetable->LocSeries.c_str() ) ); PyDict_SetItemString( dict, "train_engineload", PyGetFloat( timetable->LocLoad ) ); - PyDict_SetItemString( dict, "train_stationindex", PyGetInt( driver->StationIndex() ) ); + PyDict_SetItemString( dict, "train_stationindex", PyGetInt( driver->iStationStart ) ); auto const stationcount { driver->StationCount() }; PyDict_SetItemString( dict, "train_stationcount", PyGetInt( stationcount ) ); if( stationcount > 0 ) { @@ -5009,8 +5009,10 @@ bool TTrain::Update( double const Deltatime ) iUnits[i] = iUnitNo; cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length() - 1]; asCarName[i] = p->name(); - bPants[iUnitNo - 1][0] = (bPants[iUnitNo - 1][0] || p->MoverParameters->PantFrontUp); - bPants[iUnitNo - 1][1] = (bPants[iUnitNo - 1][1] || p->MoverParameters->PantRearUp); + if( p->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { + bPants[iUnitNo - 1][side::front] = ( bPants[iUnitNo - 1][side::front] || p->MoverParameters->PantFrontUp ); + bPants[iUnitNo - 1][side::rear] = ( bPants[iUnitNo - 1][side::rear] || p->MoverParameters->PantRearUp ); + } bComp[iUnitNo - 1][0] = (bComp[iUnitNo - 1][0] || p->MoverParameters->CompressorAllow || (p->MoverParameters->CompressorStart == start_t::automatic)); bSlip[i] = p->MoverParameters->SlippingWheels; if (p->MoverParameters->CompressorSpeed > 0.00001) diff --git a/renderer.cpp b/renderer.cpp index c866d4f1..e9e16df2 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -789,9 +789,15 @@ opengl_renderer::Render_pass( rendermode const Mode ) { bool opengl_renderer::Render_reflections() { + if( Global.ReflectionUpdatesPerSecond == 0 ) { return false; } + auto const &time = simulation::Time.data(); - auto const timestamp = time.wDay * 24 * 60 + time.wHour * 60 + time.wMinute; - if( ( timestamp - m_environmentupdatetime < 5 ) + auto const timestamp = + time.wMilliseconds + + time.wSecond * 1000 + + time.wMinute * 1000 * 60 + + time.wHour * 1000 * 60 * 60; + if( ( timestamp - m_environmentupdatetime < Global.ReflectionUpdatesPerSecond ) && ( 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; @@ -2578,6 +2584,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances float const distancefactor { std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ) }; auto const pointsize { std::max( 3.f, 5.f * distancefactor * anglefactor ) }; + auto const resolutionratio { Global.iWindowHeight / 1080.f }; // additionally reduce light strength for farther sources in rain or snow if( Global.Overcast > 0.75f ) { float const precipitationfactor{ @@ -2621,7 +2628,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { clamp( Global.fFogEnd / 2000, 0.f, 1.f ) ) * std::max( 1.f, Global.Overcast ) }; - ::glPointSize( pointsize * fogfactor ); + ::glPointSize( pointsize * resolutionratio * fogfactor ); ::glColor4f( lightcolor[ 0 ], lightcolor[ 1 ], @@ -2631,7 +2638,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { m_geometry.draw( Submodel->m_geometry ); ::glDepthMask( GL_TRUE ); } - ::glPointSize( pointsize ); + ::glPointSize( pointsize * resolutionratio ); ::glColor4f( lightcolor[ 0 ], lightcolor[ 1 ], diff --git a/translation.cpp b/translation.cpp index 469ce887..78e7dd07 100644 --- a/translation.cpp +++ b/translation.cpp @@ -168,7 +168,7 @@ init() { "Nastawnik: %2d+%d %c%s", " Predkosc: %d km/h (limit %d km/h%s)%s", ", nowy limit: %d km/h za %.1f km", - " Pochylenie: %.1f%%%%", + " Nachylenie: %.1f%%%%", "Hamulce: %4.1f+%-2.0f%c%s", " Cisnienie: %.2f kPa (przewod glowny: %.2f kPa)", "!CZUWAK! ", diff --git a/version.h b/version.h index 88afd941..2c3d40ad 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once -#define VERSION_MAJOR 18 -#define VERSION_MINOR 1231 +#define VERSION_MAJOR 19 +#define VERSION_MINOR 103 #define VERSION_REVISION 0 From 84830882c53db835a4ba02da746037c425f16614 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 12 Jan 2019 16:50:20 +0100 Subject: [PATCH 2/3] generic cab control state indicators, ai master controller logic enhancements, battery charging for diesel powered vehicles workaround, water heater damage fix, ai route scanning fixes, material loading fix --- Driver.cpp | 180 ++++++++++++++++++++++++++++----------------- Driver.h | 4 +- DynObj.cpp | 4 +- McZapkie/Mover.cpp | 21 +++--- Train.cpp | 65 +++++++++++----- Train.h | 1 + drivermode.cpp | 1 + material.cpp | 6 +- 8 files changed, 179 insertions(+), 103 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index b4236a09..c2d633cb 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -182,9 +182,9 @@ TSpeedPos::TSpeedPos(TTrack *track, double dist, int flag) Set(track, dist, flag); }; -TSpeedPos::TSpeedPos(basic_event *event, double dist, TOrders order) +TSpeedPos::TSpeedPos(basic_event *event, double dist, double length, TOrders order) { - Set(event, dist, order); + Set(event, dist, length, order); }; void TSpeedPos::Clear() @@ -366,13 +366,20 @@ bool TSpeedPos::IsProperSemaphor(TOrders order) return false; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej } -bool TSpeedPos::Set(basic_event *event, double dist, TOrders order) +bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders order) { // zapamiętanie zdarzenia fDist = dist; - iFlags = spEnabled | spEvent; // event+istotny + iFlags = spEvent; evEvent = event; vPos = event->input_location(); // współrzędne eventu albo komórki pamięci (zrzutować na tor?) - CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości + if( dist + length >= 0 ) { + iFlags |= spEnabled; + CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości + } + else { + // located behind the tracking consist, don't bother with it + return false; + } // zależnie od trybu sprawdzenie czy jest tutaj gdzieś semafor lub tarcza manewrowa // jeśli wskazuje stop wtedy wystawiamy true jako koniec sprawdzania // WriteLog("EventSet: Vel=" + AnsiString(fVelNext) + " iFlags=" + AnsiString(iFlags) + " order="+AnsiString(order)); @@ -582,6 +589,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if( newspeedpoint.Set( pEvent, GetDistanceToEvent( pTrack, pEvent, fLastDir, fCurrentDistance ), + fLength, OrderCurrentGet() ) ) { fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować @@ -1911,72 +1919,83 @@ void TController::AutoRewident() mvOccupied->BrakeOpModeFlag = i; } } + // teraz zerujemy tabelkę opóźnienia hamowania for (int i = 0; i < BrakeAccTableSize; ++i) { fBrake_a0[i+1] = 0; fBrake_a1[i+1] = 0; } - // 4. Przeliczanie siły hamowania - double const velstep = ( mvOccupied->Vmax*0.5 ) / BrakeAccTableSize; - d = pVehicles[0]; // pojazd na czele składu - while (d) { - 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); - } - IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 ); - IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 ); - - BrakingInitialLevel = ( - IsHeavyCargoTrain ? 1.25 : - IsCargoTrain ? 1.25 : - 1.00 ); - - BrakingLevelIncrease = ( - IsHeavyCargoTrain ? 0.25 : - IsCargoTrain ? 0.25 : - 0.25 ); - - if( mvOccupied->TrainType == dt_EZT ) { - if( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) { - // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors - fNominalAccThreshold = std::max( -0.60, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + if( OrderCurrentGet() & Shunt ) { + // for uniform behaviour and compatibility with older scenarios set default acceleration table values for shunting + fAccThreshold = ( + mvOccupied->TrainType == dt_EZT ? -0.55 : + mvOccupied->TrainType == dt_DMU ? -0.45 : + -0.2 ); + // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors + if( ( mvOccupied->TrainType == dt_EZT ) + && ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) ) { + fAccThreshold += 0.10; } - else { - fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); - } - fBrakeReaction = 0.25; - } - else if( mvOccupied->TrainType == dt_DMU ) { - fNominalAccThreshold = std::max( -0.45, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); - fBrakeReaction = 0.25; } - else if (ustaw > 16) { - fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 4 * fBrake_a1[ BrakeAccTableSize ]; - fBrakeReaction = 1.00 + fLength*0.004; - } - else { - fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 1 * fBrake_a1[ BrakeAccTableSize ]; - fBrakeReaction = 1.00 + fLength*0.005; - } - fAccThreshold = fNominalAccThreshold; -/* - if( IsHeavyCargoTrain ) { - // HACK: heavy cargo trains don't activate brakes early enough - fAccThreshold = std::max( -0.2, fAccThreshold ); + + if( OrderCurrentGet() & Obey_train ) { + // 4. Przeliczanie siły hamowania + double const velstep = ( mvOccupied->Vmax*0.5 ) / BrakeAccTableSize; + d = pVehicles[0]; // pojazd na czele składu + while (d) { + 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); + } + + IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 ); + IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 ); + + BrakingInitialLevel = ( + IsHeavyCargoTrain ? 1.25 : + IsCargoTrain ? 1.25 : + 1.00 ); + + BrakingLevelIncrease = ( + IsHeavyCargoTrain ? 0.25 : + IsCargoTrain ? 0.25 : + 0.25 ); + + if( mvOccupied->TrainType == dt_EZT ) { + if( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) { + // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors + fNominalAccThreshold = std::max( -0.60, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + } + else { + fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + } + fBrakeReaction = 0.25; + } + else if( mvOccupied->TrainType == dt_DMU ) { + fNominalAccThreshold = std::max( -0.45, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + fBrakeReaction = 0.25; + } + else if (ustaw > 16) { + fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 4 * fBrake_a1[ BrakeAccTableSize ]; + fBrakeReaction = 1.00 + fLength*0.004; + } + else { + fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 1 * fBrake_a1[ BrakeAccTableSize ]; + fBrakeReaction = 1.00 + fLength*0.005; + } + fAccThreshold = fNominalAccThreshold; } -*/ } double TController::ESMVelocity(bool Main) @@ -2854,7 +2873,11 @@ bool TController::IncSpeed() auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 125 : 100 ) * 1000.0 }; auto const seriesmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 ) }; auto const parallelmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) }; - auto const useseriesmodevoltage { mvControlling->EnginePowerSource.CollectorParameters.MaxV * ( IsHeavyCargoTrain ? 0.70 : 0.80 ) }; + auto const useseriesmodevoltage { + interpolate( + mvControlling->EnginePowerSource.CollectorParameters.MinV, + mvControlling->EnginePowerSource.CollectorParameters.MaxV, + ( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) }; auto const useseriesmode = ( ( mvControlling->Imax > mvControlling->ImaxLo ) || ( fVoltage < useseriesmodevoltage ) @@ -2885,7 +2908,7 @@ bool TController::IncSpeed() if( usefieldshunting ) { // to dać bocznik // engage the shuntfield only if there's sufficient power margin to draw from - auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 50.0 ) }; + auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) }; OK = ( sufficientpowermargin ? @@ -2898,7 +2921,14 @@ bool TController::IncSpeed() mvControlling->DecScndCtrl( 2 ); } // kręcimy nastawnik jazdy - auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 80.0 : 40.0 ) }; + // don't draw too much power; + // keep from dropping into series mode when entering/using parallel mode, and from shutting down in the series mode + auto const sufficientpowermargin { + fVoltage - ( + mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ? + mvControlling->EnginePowerSource.CollectorParameters.MinV : + useseriesmodevoltage ) + > ( IsHeavyCargoTrain ? 80.0 : 60.0 ) }; OK = ( ( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ? @@ -3909,12 +3939,13 @@ TController::UpdateSituation(double dt) { p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła) } - // crude way to deal with automatic door opening on W4 preventing further ride + // HACK: 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 + // TODO: check if this situation still happens and the hack is still needed if( ( false == AIControllFlag ) && ( iDrivigFlags & moveDoorOpened ) && ( mvOccupied->DoorCloseCtrl != control_t::driver ) - && ( mvControlling->MainCtrlPos > 0 ) ) { + && ( mvControlling->MainCtrlPos > ( mvControlling->EngineType != TEngineType::DieselEngine ? 0 : 1 ) ) ) { // for diesel 1st position is effectively 0 Doors( false ); } @@ -4013,7 +4044,12 @@ TController::UpdateSituation(double dt) { } } - auto const useseriesmodevoltage { mvControlling->EnginePowerSource.CollectorParameters.MaxV * ( IsHeavyCargoTrain ? 0.70 : 0.80 ) }; + // TODO: refactor this calculation into a subroutine + auto const useseriesmodevoltage { + interpolate( + mvControlling->EnginePowerSource.CollectorParameters.MinV, + mvControlling->EnginePowerSource.CollectorParameters.MaxV, + ( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) }; if( fVoltage <= useseriesmodevoltage ) { // if the power station is heavily burdened try to reduce the load @@ -5865,12 +5901,20 @@ basic_event * TController::CheckTrackEventBackward(double fDirection, TTrack *Tr { // sprawdzanie eventu w torze, czy jest sygnałowym - skanowanie do tyłu // NOTE: this method returns only one event which meets the conditions, due to limitations in the caller // TBD, TODO: clean up the caller and return all suitable events, as in theory things will go awry if the track has more than one signal + auto const dir{ pVehicles[ 0 ]->VectorFront() * pVehicles[ 0 ]->DirectionGet() }; + auto const pos{ pVehicles[ 0 ]->HeadPosition() }; auto const &eventsequence { ( fDirection > 0 ? Track->m_events2 : Track->m_events1 ) }; for( auto const &event : eventsequence ) { if( ( event.second != nullptr ) && ( event.second->m_passive ) && ( typeid(*(event.second)) == typeid( getvalues_event ) ) ) { - return event.second; + // since we're checking for events behind us discard the sources in front of the scanning vehicle + auto const sl{ event.second->input_location() }; // położenie komórki pamięci + auto const sem{ sl - pos }; // wektor do komórki pamięci od końca składu + if( dir.x * sem.x + dir.z * sem.z < 0 ) { + // iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu + return event.second; + } } } return nullptr; diff --git a/Driver.h b/Driver.h index 9140a899..e6fa6125 100644 --- a/Driver.h +++ b/Driver.h @@ -141,7 +141,7 @@ class TSpeedPos public: TSpeedPos(TTrack *track, double dist, int flag); - TSpeedPos(basic_event *event, double dist, TOrders order); + TSpeedPos(basic_event *event, double dist, double length, TOrders order); TSpeedPos() = default; void Clear(); bool Update(); @@ -150,7 +150,7 @@ class TSpeedPos void UpdateDistance( double dist ) { fDist -= dist; } - bool Set(basic_event *e, double d, TOrders order = Wait_for_orders); + bool Set(basic_event *e, double d, double length, TOrders order = Wait_for_orders); void Set(TTrack *t, double d, int f); std::string TableText() const; std::string GetName() const; diff --git a/DynObj.cpp b/DynObj.cpp index bbc68141..5d56d0d3 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2782,7 +2782,7 @@ TDynamicObject::update_load_visibility() { } */ auto loadpercentage { ( - MoverParameters->MaxLoad == 0.0 ? + MoverParameters->MaxLoad == 0.f ? 0.0 : 100.0 * MoverParameters->LoadAmount / MoverParameters->MaxLoad ) }; auto const sectionloadpercentage { ( @@ -2816,7 +2816,7 @@ TDynamicObject::update_load_offset() { if( MoverParameters->LoadType.offset_min == 0.f ) { return; } auto const loadpercentage { ( - MoverParameters->MaxLoad == 0.0 ? + MoverParameters->MaxLoad == 0.f ? 0.0 : 100.0 * MoverParameters->LoadAmount / MoverParameters->MaxLoad ) }; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index d0918c15..9fbe5b3d 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -812,10 +812,13 @@ void TMoverParameters::UpdateBatteryVoltage(double dt) // HACK: allow to draw power also from adjacent converter, applicable for EMUs // TODO: expand power cables system to include low voltage power transfers + // HACK: emulate low voltage generator powered directly by the diesel engine auto const converteractive{ ( ( ConverterFlag ) || ( ( ( Couplers[ side::front ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::front ].Connected->ConverterFlag ) - || ( ( ( Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::rear ].Connected->ConverterFlag ) ) }; + || ( ( ( Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::rear ].Connected->ConverterFlag ) ) + || ( ( EngineType == TEngineType::DieselElectric ) && ( true == Mains ) ) + || ( ( EngineType == TEngineType::DieselEngine ) && ( true == Mains ) ) }; if ((NominalBatteryVoltage / BatteryVoltage < 1.22) && Battery) { // 110V @@ -1529,18 +1532,18 @@ void TMoverParameters::WaterPumpCheck( double const Timestep ) { // water heater status check void TMoverParameters::WaterHeaterCheck( double const Timestep ) { - WaterHeater.is_damaged = ( - ( true == WaterHeater.is_damaged ) - || ( ( true == WaterHeater.is_active ) - && ( false == WaterPump.is_active ) ) ); - WaterHeater.is_active = ( ( false == WaterHeater.is_damaged ) && ( true == Battery ) && ( true == WaterHeater.is_enabled ) && ( true == WaterHeater.breaker ) && ( ( WaterHeater.is_active ) || ( WaterHeater.config.temp_min < 0 ) || ( dizel_heat.temperatura1 < WaterHeater.config.temp_min ) ) ); - + + WaterHeater.is_damaged = ( + ( true == WaterHeater.is_damaged ) + || ( ( true == WaterHeater.is_active ) + && ( false == WaterPump.is_active ) ) ); + if( ( WaterHeater.config.temp_max > 0 ) && ( dizel_heat.temperatura1 > WaterHeater.config.temp_max ) ) { WaterHeater.is_active = false; @@ -6664,7 +6667,7 @@ TMoverParameters::AssignLoad( std::string const &Name, float const Amount ) { for( auto const &loadattributes : LoadAttributes ) { if( Name == loadattributes.name ) { LoadType = loadattributes; - LoadAmount = Amount; + LoadAmount = clamp( Amount, 0.f, MaxLoad ) ; return true; } } @@ -6702,7 +6705,7 @@ bool TMoverParameters::LoadingDone(double const LSpeed, std::string const &Loadn if( ( LoadAmount <= 0 ) || ( CommandIn.Value1 <= 0 ) ) { // pusto lub rozładowano żądaną ilość LoadStatus = 4; // skończony rozładunek - LoadAmount = std::max( 0.f, LoadAmount ); //ładunek nie może być ujemny + LoadAmount = clamp( LoadAmount, 0.f, MaxLoad); //ładunek nie może być ujemny } if( LoadAmount == 0.f ) { AssignLoad(""); // jak nic nie ma, to nie ma też nazwy diff --git a/Train.cpp b/Train.cpp index 3fe0e1d4..2a9e8a92 100644 --- a/Train.cpp +++ b/Train.cpp @@ -448,9 +448,9 @@ PyObject *TTrain::GetTrainState() { // basic systems state data PyDict_SetItemString( dict, "battery", PyGetBool( mvControlled->Battery ) ); PyDict_SetItemString( dict, "linebreaker", PyGetBool( mvControlled->Mains ) ); - PyDict_SetItemString( dict, "converter", PyGetBool( mover->ConverterFlag ) ); - PyDict_SetItemString( dict, "converter_overload", PyGetBool( mover->ConvOvldFlag ) ); - PyDict_SetItemString( dict, "compress", PyGetBool( mover->CompressorFlag ) ); + PyDict_SetItemString( dict, "converter", PyGetBool( mvControlled->ConverterFlag ) ); + PyDict_SetItemString( dict, "converter_overload", PyGetBool( mvControlled->ConvOvldFlag ) ); + PyDict_SetItemString( dict, "compress", PyGetBool( mvControlled->CompressorFlag ) ); // reverser PyDict_SetItemString( dict, "direction", PyGetInt( mover->ActiveDir ) ); // throttle @@ -463,34 +463,35 @@ PyObject *TTrain::GetTrainState() { bool const bEP = ( mvControlled->LocHandle->GetCP() > 0.2 ) || ( fEIMParams[ 0 ][ 2 ] > 0.01 ); PyDict_SetItemString( dict, "dir_brake", PyGetBool( bEP ) ); bool bPN; - if( ( typeid( *mvControlled->Hamulec ) == typeid( TLSt ) ) - || ( typeid( *mvControlled->Hamulec ) == typeid( TEStED ) ) ) { + if( ( typeid( *mvOccupied->Hamulec ) == typeid( TLSt ) ) + || ( typeid( *mvOccupied->Hamulec ) == typeid( TEStED ) ) ) { - TBrake* temp_ham = mvControlled->Hamulec.get(); + TBrake* temp_ham = mvOccupied->Hamulec.get(); bPN = ( static_cast( temp_ham )->GetEDBCP() > 0.2 ); } else bPN = false; PyDict_SetItemString( dict, "indir_brake", PyGetBool( bPN ) ); - PyDict_SetItemString( dict, "brake_delay_flag", PyGetInt( mvControlled->BrakeDelayFlag )); - PyDict_SetItemString( dict, "brake_op_mode_flag", PyGetInt( mvControlled->BrakeOpModeFlag )); + PyDict_SetItemString( dict, "brake_delay_flag", PyGetInt( mvOccupied->BrakeDelayFlag )); + PyDict_SetItemString( dict, "brake_op_mode_flag", PyGetInt( mvOccupied->BrakeOpModeFlag )); // other controls PyDict_SetItemString( dict, "ca", PyGetBool( TestFlag( mvOccupied->SecuritySystem.Status, s_aware ) ) ); PyDict_SetItemString( dict, "shp", PyGetBool( TestFlag( mvOccupied->SecuritySystem.Status, s_active ) ) ); PyDict_SetItemString( dict, "pantpress", PyGetFloat( mvControlled->PantPress ) ); PyDict_SetItemString( dict, "universal3", PyGetBool( InstrumentLightActive ) ); PyDict_SetItemString( dict, "radio_channel", PyGetInt( iRadioChannel ) ); + PyDict_SetItemString( dict, "door_lock", PyGetInt( mvOccupied->DoorLockEnabled ) ); // movement data PyDict_SetItemString( dict, "velocity", PyGetFloat( mover->Vel ) ); PyDict_SetItemString( dict, "tractionforce", PyGetFloat( mover->Ft ) ); PyDict_SetItemString( dict, "slipping_wheels", PyGetBool( mover->SlippingWheels ) ); PyDict_SetItemString( dict, "sanding", PyGetBool( mover->SandDose ) ); // electric current data - PyDict_SetItemString( dict, "traction_voltage", PyGetFloat( mover->RunningTraction.TractionVoltage ) ); - PyDict_SetItemString( dict, "voltage", PyGetFloat( mover->Voltage ) ); - PyDict_SetItemString( dict, "im", PyGetFloat( mover->Im ) ); - PyDict_SetItemString( dict, "fuse", PyGetBool( mover->FuseFlag ) ); - PyDict_SetItemString( dict, "epfuse", PyGetBool( mover->EpFuse ) ); + PyDict_SetItemString( dict, "traction_voltage", PyGetFloat( mvControlled->RunningTraction.TractionVoltage ) ); + PyDict_SetItemString( dict, "voltage", PyGetFloat( mvControlled->Voltage ) ); + PyDict_SetItemString( dict, "im", PyGetFloat( mvControlled->Im ) ); + PyDict_SetItemString( dict, "fuse", PyGetBool( mvControlled->FuseFlag ) ); + PyDict_SetItemString( dict, "epfuse", PyGetBool( mvOccupied->EpFuse ) ); // induction motor state data char const *TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" }; char const *TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" }; @@ -575,6 +576,7 @@ PyObject *TTrain::GetTrainState() { PyDict_SetItemString( dict, "minutes", PyGetInt( simulation::Time.data().wMinute ) ); PyDict_SetItemString( dict, "seconds", PyGetInt( simulation::Time.second() ) ); PyDict_SetItemString( dict, "air_temperature", PyGetInt( Global.AirTemperature ) ); + PyDict_SetItemString( dict, "light_level", PyGetFloat( Global.fLuminance - std::max( 0.f, Global.Overcast - 1.f ) ) ); Application.release_python_lock(); return dict; @@ -1041,7 +1043,7 @@ void TTrain::OnCommand_trainbrakeincrease( TTrain *Train, command_data const &Co if( Command.action != GLFW_RELEASE ) { if( Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a ) { - Train->mvOccupied->BrakeLevelAdd( 0.1 /*15.0 * Command.time_delta*/ ); + Train->mvOccupied->BrakeLevelAdd( 0.1 * Global.fBrakeStep ); } else { Train->set_train_brake( Train->mvOccupied->BrakeCtrlPos + Global.fBrakeStep ); @@ -1054,7 +1056,7 @@ void TTrain::OnCommand_trainbrakedecrease( TTrain *Train, command_data const &Co if( Command.action != GLFW_RELEASE ) { // press or hold if( Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a ) { - Train->mvOccupied->BrakeLevelAdd( -0.1 /*-15.0 * Command.time_delta*/ ); + Train->mvOccupied->BrakeLevelAdd( -0.1 * Global.fBrakeStep ); } else { Train->set_train_brake( Train->mvOccupied->BrakeCtrlPos - Global.fBrakeStep ); @@ -4085,14 +4087,14 @@ void TTrain::OnCommand_generictoggle( TTrain *Train, command_data const &Command auto const itemindex = static_cast( Command.command ) - static_cast( user_command::generictoggle0 ); auto &item = Train->ggUniversals[ itemindex ]; - +/* if( item.SubModel == nullptr ) { if( Command.action == GLFW_PRESS ) { WriteLog( "Train generic item " + std::to_string( itemindex ) + " is missing, or wasn't defined" ); } return; } - +*/ if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( item.GetDesiredValue() < 0.5 ) { @@ -5475,6 +5477,10 @@ bool TTrain::Update( double const Deltatime ) // others btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); btLampkaMotorBlowers.Turn( ( mvControlled->MotorBlowers[ side::front ].is_active ) && ( mvControlled->MotorBlowers[ side::rear ].is_active ) ); + // universal devices state indicators + for( auto idx = 0; idx < btUniversals.size(); ++idx ) { + btUniversals[ idx ].Turn( ggUniversals[ idx ].GetValue() > 0.5 ); + } } else { // wylaczone @@ -5530,6 +5536,10 @@ bool TTrain::Update( double const Deltatime ) // others btLampkaMalfunction.Turn( false ); btLampkaMotorBlowers.Turn( false ); + // universal devices state indicators + for( auto &universal : btUniversals ) { + universal.Turn( false ); + } } { // yB - wskazniki drugiego czlonu @@ -6184,7 +6194,9 @@ void TTrain::update_sounds_runningnoise( sound_source &Sound ) { // volume calculation auto volume = Sound.m_amplitudeoffset - + Sound.m_amplitudefactor * mvOccupied->Vel; + + Sound.m_amplitudefactor * interpolate( + mvOccupied->Vel / ( 1 + mvOccupied->Vmax ), 1.0, + 0.5 ); // scale base volume between 0.5-1.0 if( std::abs( mvOccupied->nrot ) > 0.01 ) { // hamulce wzmagaja halas auto const brakeforceratio { ( @@ -6208,7 +6220,7 @@ void TTrain::update_sounds_runningnoise( sound_source &Sound ) { interpolate( 0.0, 1.0, clamp( - mvOccupied->Vel / 40.0, + mvOccupied->Vel / 25.0, 0.0, 1.0 ) ); } @@ -7075,6 +7087,9 @@ void TTrain::clear_cab_controls() btLampkaHamulecReczny.Clear(); btLampkaBlokadaDrzwi.Clear(); btLampkaDoorLockOff.Clear(); + for( auto &universal : btUniversals ) { + universal.Clear(); + } btInstrumentLight.Clear(); btDashboardLight.Clear(); btTimetableLight.Clear(); @@ -7507,7 +7522,17 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-rearrightend:", btLampkaRearRightEndLight }, { "i-dashboardlight:", btDashboardLight }, { "i-timetablelight:", btTimetableLight }, - { "i-cablight:", btCabLight } + { "i-cablight:", btCabLight }, + { "i-universal0:", btUniversals[ 0 ] }, + { "i-universal1:", btUniversals[ 1 ] }, + { "i-universal2:", btUniversals[ 2 ] }, + { "i-universal3:", btUniversals[ 3 ] }, + { "i-universal4:", btUniversals[ 4 ] }, + { "i-universal5:", btUniversals[ 5 ] }, + { "i-universal6:", btUniversals[ 6 ] }, + { "i-universal7:", btUniversals[ 7 ] }, + { "i-universal8:", btUniversals[ 8 ] }, + { "i-universal9:", btUniversals[ 9 ] } }; auto lookup = lights.find( Label ); if( lookup != lights.end() ) { diff --git a/Train.h b/Train.h index 0279f257..a398ec20 100644 --- a/Train.h +++ b/Train.h @@ -505,6 +505,7 @@ public: // reszta może by?publiczna TButton btLampkaHamowanie2zes; TButton btLampkaOpory; TButton btLampkaWysRozr; + std::array btUniversals; // NOTE: temporary arrangement until we have dynamically built control table TButton btInstrumentLight; TButton btDashboardLight; TButton btTimetableLight; diff --git a/drivermode.cpp b/drivermode.cpp index 94029c02..6b4d576d 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -1133,6 +1133,7 @@ driver_mode::InOutKey() if( train == nullptr ) { FreeFlyModeFlag = true; // nadal poza kabiną + Camera.m_owner = nullptr; // detach camera from the vehicle return; } diff --git a/material.cpp b/material.cpp index 9ea3fbd9..91db805b 100644 --- a/material.cpp +++ b/material.cpp @@ -60,17 +60,19 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c } else if( ( key == "texture1:" ) || ( key == "texture_diffuse:" ) ) { + auto const value { deserialize_random_set( Input ) }; if( ( texture1 == null_handle ) || ( Priority > priority1 ) ) { - texture1 = GfxRenderer.Fetch_Texture( deserialize_random_set( Input ), Loadnow ); + texture1 = GfxRenderer.Fetch_Texture( value, Loadnow ); priority1 = Priority; } } else if( ( key == "texture2:" ) || ( key == "texture_normalmap:" ) ) { + auto const value { deserialize_random_set( Input ) }; if( ( texture2 == null_handle ) || ( Priority > priority2 ) ) { - texture2 = GfxRenderer.Fetch_Texture( deserialize_random_set( Input ), Loadnow ); + texture2 = GfxRenderer.Fetch_Texture( value, Loadnow ); priority2 = Priority; } } From 6f48a6e55dbce7a1b9e16a49dbcf2dff0d94ca48 Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 12 Jan 2019 01:40:22 +0100 Subject: [PATCH 3/3] lazy bounding radius computing for shape_node (avoids O(n^2) deserialization) --- scene.cpp | 4 ++-- scenenode.cpp | 12 ++++++++++-- scenenode.h | 7 ++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/scene.cpp b/scene.cpp index 5d937723..cdc06b43 100644 --- a/scene.cpp +++ b/scene.cpp @@ -259,7 +259,7 @@ basic_cell::insert( shape_node Shape ) { // re-calculate cell radius, in case shape geometry extends outside the cell's boundaries m_area.radius = std::max( m_area.radius, - glm::length( m_area.center - Shape.data().area.center ) + Shape.data().area.radius ); + glm::length( m_area.center - Shape.data().area.center ) + Shape.radius() ); auto const &shapedata { Shape.data() }; auto &shapes = ( @@ -1230,7 +1230,7 @@ basic_region::insert( shape_node Shape, scratch_data &Scratchpad, bool const Tra // move the data into appropriate section(s) for( auto &shape : shapes ) { // with the potential splitting done we can calculate each chunk's bounding radius - shape.compute_radius(); + shape.invalidate_radius(); if( point_inside( shape.m_data.area.center ) ) { // NOTE: nodes placed outside of region boundaries are discarded section( shape.m_data.area.center ).insert( shape ); diff --git a/scenenode.cpp b/scenenode.cpp index ebb7aa87..eeb2ae98 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -410,8 +410,7 @@ shape_node::merge( shape_node &Shape ) { m_data.vertices.insert( std::end( m_data.vertices ), std::begin( Shape.m_data.vertices ), std::end( Shape.m_data.vertices ) ); - // NOTE: we could recalculate radius with something other than brute force, but it'll do - compute_radius(); + invalidate_radius(); return true; } @@ -445,7 +444,16 @@ shape_node::compute_radius() { m_data.area.radius = static_cast( std::sqrt( squaredradius ) ); } +void shape_node::invalidate_radius() { + m_data.area.radius = -1.0f; +} +float shape_node::radius() { + if (m_data.area.radius == -1.0f) + compute_radius(); + + return m_data.area.radius; +} // sends content of the struct to provided stream void diff --git a/scenenode.h b/scenenode.h index 2bd0d2c2..65be6507 100644 --- a/scenenode.h +++ b/scenenode.h @@ -126,7 +126,10 @@ public: create_geometry( gfx::geometrybank_handle const &Bank ); // calculates shape's bounding radius void - compute_radius(); + compute_radius(); + // invalidates shape's bounding radius + void + invalidate_radius(); // set visibility void visible( bool State ); @@ -136,6 +139,8 @@ public: // data access shapenode_data const & data() const; + // get bounding radius + float radius(); private: // members