diff --git a/Driver.cpp b/Driver.cpp index 7e4693b3..284d84f8 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1111,7 +1111,7 @@ TController::TableUpdateStopPoint( TCommandType &Command, TSpeedPos &Point, doub // nastepnie ustawić następną na aktualną tak żeby prawidłowo ją obsłużył w następnym kroku if( true == TrainParams.RewindTimeTable( Point.evEvent->input_text() ) ) { asNextStop = TrainParams.NextStop(); - iStationStart = TrainParams.StationIndex; + TrainParams.StationStart = TrainParams.StationIndex; } } else if( Point.fDist < -fLength ) { @@ -2520,6 +2520,30 @@ bool TController::CheckVehicles(TOrders user) = 0; } + if( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) { + // nastawianie hamulca do jazdy pociągowej + AutoRewident(); +/* + if( ( true == TestFlag( iDrivigFlags, moveConnect ) ) + && ( true == TestFlag( OrderCurrentGet(), Connect ) ) ) { + iCoupler = 0; // dalsza jazda manewrowa już bez łączenia + iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania + SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie + JumpToNextOrder(); // wykonanie następnej komendy + } +*/ + } + + // detect push-pull train configurations and mark them accordingly + if( pVehicles[ end::front ]->is_connected( pVehicles[ end::rear ], coupling::control ) ) { + // zmiana czoła przez zmianę kabiny + iDrivigFlags |= movePushPull; + } + else { + // zmiana czoła przez manewry + iDrivigFlags &= ~movePushPull; + } + // HACK: ensure vehicle lights are active from the beginning, if it had pre-activated battery if( mvOccupied->LightsPosNo > 0 ) { pVehicle->SetLights(); @@ -2565,29 +2589,6 @@ bool TController::CheckVehicles(TOrders user) } } } - if( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) { - // nastawianie hamulca do jazdy pociągowej - AutoRewident(); -/* - if( ( true == TestFlag( iDrivigFlags, moveConnect ) ) - && ( true == TestFlag( OrderCurrentGet(), Connect ) ) ) { - iCoupler = 0; // dalsza jazda manewrowa już bez łączenia - iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania - SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie - JumpToNextOrder(); // wykonanie następnej komendy - } -*/ - } - - // detect push-pull train configurations and mark them accordingly - if( pVehicles[ end::front ]->is_connected( pVehicles[ end::rear ], coupling::control ) ) { - // zmiana czoła przez zmianę kabiny - iDrivigFlags |= movePushPull; - } - else { - // zmiana czoła przez manewry - iDrivigFlags &= ~movePushPull; - } if( ( user == Connect ) || ( user == Disconnect ) ) { @@ -2840,10 +2841,12 @@ bool TController::PrepareEngine() else { // main circuit or engine is on, set up vehicle devices and controls if( false == IsAnyConverterOverloadRelayOpen ) { - cue_action( locale::string::driver_hint_converteron ); + if( IsAnyConverterPresent ) { + cue_action( locale::string::driver_hint_converteron ); + } // w EN57 sprężarka w ra jest zasilana z silnikowego // TODO: change condition to presence of required voltage type - if( IsAnyConverterEnabled ) { + if( IsAnyCompressorPresent && IsAnyConverterEnabled ) { cue_action( locale::string::driver_hint_compressoron ); } if( ( mvControlling->ScndPipePress < 4.5 ) && ( mvControlling->VeselVolume > 0.0 ) ) { @@ -2881,8 +2884,8 @@ bool TController::PrepareEngine() isready = ( false == IsAnyConverterOverloadRelayOpen ) && ( mvOccupied->DirActive != 0 ) && ( false == IsAnyLineBreakerOpen ) - && ( true == IsAnyConverterEnabled ) - && ( true == IsAnyCompressorEnabled ) + && ( ( false == IsAnyConverterPresent ) || ( true == IsAnyConverterEnabled ) ) + && ( ( false == IsAnyCompressorPresent ) || ( true == IsAnyCompressorEnabled ) ) && ( ( mvControlling->ScndPipePress > 4.5 ) || ( mvControlling->VeselVolume == 0.0 ) ) && ( ( static_cast( mvOccupied->fBrakeCtrlPos ) == static_cast( mvOccupied->Handle->GetPos( bh_RP ) ) ) || ( static_cast( mvOccupied->fBrakeCtrlPos ) != static_cast( mvOccupied->Handle->GetPos( bh_NP ) ) ) @@ -3403,13 +3406,17 @@ bool TController::IncSpeed() iDrivigFlags |= moveIncSpeed; // ustawienie flagi jazdy return false; case TEngineType::ElectricSeriesMotor: - if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) // jeśli pantografujący + if (mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) // jeśli pantografujący { if (fOverhead2 >= 0.0) // a jazda bezprądowa ustawiana eventami (albo opuszczenie) return false; // to nici z ruszania if (iOverheadZero) // jazda bezprądowa z poziomu toru ustawia bity return false; // to nici z ruszania } + // TODO: move all fVoltage assignments to a single, more generic place + if( mvControlling->EnginePowerSource.SourceType == TPowerSource::Accumulator ) { + fVoltage = mvControlling->BatteryVoltage; + } if ((!IsAnyMotorOverloadRelayOpen) &&(!mvControlling->ControlPressureSwitch)) { if ((mvControlling->IsMainCtrlNoPowerPos()) @@ -3437,15 +3444,17 @@ bool TController::IncSpeed() auto const sufficientacceleration { AbsAccS >= ( IsHeavyCargoTrain ? 0.03 : IsCargoTrain ? 0.06 : 0.09 ) }; 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 { + auto const minvoltage { ( mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? mvPantographUnit->EnginePowerSource.CollectorParameters.MinV : 0.0 ) }; + auto const maxvoltage { ( mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? mvPantographUnit->EnginePowerSource.CollectorParameters.MaxV : 0.0 ) }; + auto const seriesmodevoltage { interpolate( - mvControlling->EnginePowerSource.CollectorParameters.MinV, - mvControlling->EnginePowerSource.CollectorParameters.MaxV, + minvoltage, + maxvoltage, ( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) }; auto const useseriesmode = ( ( mvControlling->Imax > mvControlling->ImaxLo ) || ( true == usehighoverloadrelaythreshold ) - || ( fVoltage < useseriesmodevoltage ) + || ( fVoltage < seriesmodevoltage ) || ( ( true == sufficientacceleration ) && ( true == sufficienttractionforce ) && ( mvOccupied->Vel <= ( IsCargoTrain ? 40 : 30 ) + ( seriesmodefieldshunting ? 5 : 0 ) - ( ( fAccGravity < -0.025 ) ? 10 : 0 ) ) ) ); @@ -3501,7 +3510,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 : 75.0 ) * ControlledEnginesCount }; + auto const sufficientpowermargin { fVoltage - seriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) * ControlledEnginesCount }; OK = ( sufficientpowermargin ? @@ -3519,8 +3528,8 @@ bool TController::IncSpeed() auto const sufficientpowermargin { fVoltage - ( mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ? - mvControlling->EnginePowerSource.CollectorParameters.MinV : - useseriesmodevoltage ) + minvoltage : + seriesmodevoltage ) > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) * ControlledEnginesCount }; OK = ( @@ -4517,7 +4526,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N TrainParams.UpdateMTable( simulation::Time, TrainParams.NextStationName ); TrainParams.StationIndexInc(); // przejście do następnej - iStationStart = TrainParams.StationIndex; + TrainParams.StationStart = TrainParams.StationIndex; asNextStop = TrainParams.NextStop(); m_lastannouncement = announcement_t::idle; iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym @@ -6063,10 +6072,10 @@ TController::determine_consist_state() { IsAnyMotorOverloadRelayOpen = false; IsAnyGroundRelayOpen = false; IsAnyLineBreakerOpen = false; - // HACK: enable a make-believe compressor in all cars - // TBD, TODO: replace with a more flexible vehicle readiness check in PrepareEngine() - IsAnyCompressorEnabled = is_car(); + IsAnyCompressorPresent = false; + IsAnyCompressorEnabled = false; IsAnyCompressorExplicitlyEnabled = false; + IsAnyConverterPresent = false; IsAnyConverterEnabled = false; IsAnyConverterExplicitlyEnabled = false; @@ -6077,8 +6086,10 @@ TController::determine_consist_state() { IsAnyConverterOverloadRelayOpen |= vehicle->ConvOvldFlag; IsAnyMotorOverloadRelayOpen |= vehicle->FuseFlag; IsAnyGroundRelayOpen |= !( vehicle->GroundRelay ); + IsAnyCompressorPresent |= ( vehicle->CompressorSpeed > 0.0 ); IsAnyCompressorEnabled |= ( vehicle->CompressorSpeed > 0.0 ? ( vehicle->CompressorAllow || vehicle->CompressorStart == start_t::automatic ) && ( vehicle->CompressorAllowLocal ) : false ); IsAnyCompressorExplicitlyEnabled |= ( vehicle->CompressorSpeed > 0.0 ? ( vehicle->CompressorAllow && vehicle->CompressorAllowLocal ) : false ); + IsAnyConverterPresent |= ( vehicle->ConverterStart != start_t::disabled ); IsAnyConverterEnabled |= ( vehicle->ConverterAllow || vehicle->ConverterStart == start_t::automatic ) && ( vehicle->ConverterAllowLocal ); IsAnyConverterExplicitlyEnabled |= ( vehicle->ConverterAllow && vehicle->ConverterAllowLocal ); if( vehicle->Power > 0.01 ) { @@ -6134,8 +6145,8 @@ TController::determine_consist_state() { iEngineActive &= ( false == IsAnyConverterOverloadRelayOpen ) && ( false == IsAnyLineBreakerOpen ) - && ( true == IsAnyConverterEnabled ) - && ( true == IsAnyCompressorEnabled ); + && ( ( false == IsAnyConverterPresent ) || ( true == IsAnyConverterEnabled ) ) + && ( ( false == IsAnyCompressorPresent ) || ( true == IsAnyCompressorEnabled ) ); } void @@ -6513,7 +6524,7 @@ TController::UpdateNextStop() { if( ( fLastStopExpDist > 0.0 ) && ( mvOccupied->DistCounter > fLastStopExpDist ) ) { // zaktualizować wyświetlanie rozkładu - iStationStart = TrainParams.StationIndex; + TrainParams.StationStart = TrainParams.StationIndex; fLastStopExpDist = -1.0; // usunąć licznik if( true == m_makenextstopannouncement ) { announce( announcement_t::next ); @@ -7901,7 +7912,7 @@ void TController::control_tractive_force() { auto const velocity { DirectionalVel() }; // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz... if( ( AccDesired > EU07_AI_NOACCELERATION ) // don't add power if not asked for actual speed-up - && (( AbsAccS < AccDesired - std::min( 0.05, 0.01 * iDriverFailCount ) ) || (mvOccupied->SpeedCtrlUnit.IsActive && velocity < mvOccupied->SpeedCtrlUnit.FullPowerVelocity)) + && (( AbsAccS < AccDesired /*- std::min( 0.05, 0.01 * iDriverFailCount )*/ ) || (mvOccupied->SpeedCtrlUnit.IsActive && velocity < mvOccupied->SpeedCtrlUnit.FullPowerVelocity)) && ( false == TestFlag( iDrivigFlags, movePress ) ) ) { // ...jeśli prędkość w kierunku czoła jest mniejsza od dozwolonej o margines... if( velocity < ( diff --git a/Driver.h b/Driver.h index 7e9e4be3..eeedc8cd 100644 --- a/Driver.h +++ b/Driver.h @@ -549,7 +549,7 @@ private: // members Mtable::TTrainParameters TrainParams; // rozkład jazdy zawsze jest, nawet jeśli pusty std::string asNextStop; // nazwa następnego punktu zatrzymania wg rozkładu - int iStationStart = 0; // numer pierwszej stacji pokazywanej na podglądzie rozkładu +// int iStationStart = 0; // numer pierwszej stacji pokazywanej na podglądzie rozkładu std::string m_lastexchangestop; // HACK: safeguard to prevent multiple load exchanges per station int m_lastexchangeplatforms { 0 }; // cached station platforms for last exchange int m_lastexchangedirection { 0 }; // @@ -595,8 +595,10 @@ private: bool IsAnyConverterOverloadRelayOpen{ false }; // state of converter overload relays in all vehicles under control bool IsAnyMotorOverloadRelayOpen{ false }; // state of motor overload relays in all vehicles under control bool IsAnyGroundRelayOpen{ false }; + bool IsAnyCompressorPresent { false }; bool IsAnyCompressorEnabled{ false }; bool IsAnyCompressorExplicitlyEnabled{ false }; // only takes into account manually controlled devices + bool IsAnyConverterPresent{ false }; bool IsAnyConverterEnabled{ false }; bool IsAnyConverterExplicitlyEnabled{ false }; // only takes into account manually controlled devices bool IsAnyCouplerStretched{ false }; // whether there's a coupler in the consist stretched above limit diff --git a/DynObj.cpp b/DynObj.cpp index c68a4dda..ced62243 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2859,7 +2859,7 @@ TDynamicObject::update_load_visibility() { auto visiblechunkcount { ( SectionLoadOrder.empty() ? 0 : - static_cast( std::ceil( loadpercentage * SectionLoadOrder.size() ) ) ) }; + static_cast( std::ceil( loadpercentage * SectionLoadOrder.size() - 0.001f ) ) ) }; for( auto *section : SectionLoadOrder ) { if( visiblechunkcount == 0 ) { break; } section->load_chunks_visible++; @@ -5620,10 +5620,10 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co blowertemplate.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); blowertemplate.owner( this ); - auto const amplitudedivisor = static_cast( - MoverParameters->MotorBlowers[ end::front ].speed > 0.f ? - MoverParameters->MotorBlowers[ end::front ].speed * MoverParameters->nmax * 60 + MoverParameters->Power * 3 : - MoverParameters->MotorBlowers[ end::front ].speed * -1 ); + auto const amplitudedivisor { static_cast( + MoverParameters->MotorBlowers[ end::front ].speed > 0 ? MoverParameters->MotorBlowers[ end::front ].speed * MoverParameters->nmax * 60 + MoverParameters->Power * 3 : + blowertemplate.has_bookends() ? 1 : // NOTE: for motorblowers with fixed speed if the sound has defined bookends we skip revolutions-based part of frequency/volume adjustments + MoverParameters->MotorBlowers[ end::front ].speed * -1 ) }; blowertemplate.m_amplitudefactor /= amplitudedivisor; blowertemplate.m_frequencyfactor /= amplitudedivisor; @@ -6031,6 +6031,11 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co >> soundproofing[ 3 ] >> soundproofing[ 4 ] >> soundproofing[ 5 ]; + for( auto & soundproofingelement : soundproofing ) { + if( soundproofingelement != -1.f ) { + soundproofingelement = std::sqrtf( clamp( soundproofingelement, 0.f, 1.f ) ); + } + } m_pasystem.soundproofing = soundproofing; continue; } @@ -7890,14 +7895,16 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub for( auto &blowersound : motorblowers ) { // match the motor blower and the sound source based on whether they're located in the front or the back of the vehicle auto const &blower { Vehicle.MotorBlowers[ ( blowersound.offset().z > 0 ? end::front : end::rear ) ] }; + // TODO: for the sounds with provided bookends invoke stop() when the stop is triggered and revolutions start dropping, instead of after full stop if( blower.revolutions > 1 ) { - + // NOTE: for motorblowers with fixed speed if the sound has defined bookends we skip revolutions-based part of frequency/volume adjustments + auto const revolutionmodifier { ( Vehicle.MotorBlowers[ end::front ].speed < 0.f ) && ( blowersound.has_bookends() ) ? 1.f : blower.revolutions }; blowersound .pitch( true == blowersound.is_combined() ? blower.revolutions * 0.01f : - blowersound.m_frequencyoffset + blowersound.m_frequencyfactor * blower.revolutions ) - .gain( blowersound.m_amplitudeoffset + blowersound.m_amplitudefactor * blower.revolutions ) + blowersound.m_frequencyoffset + blowersound.m_frequencyfactor * revolutionmodifier ) + .gain( blowersound.m_amplitudeoffset + blowersound.m_amplitudefactor * revolutionmodifier ) .play( sound_flags::exclusive | sound_flags::looping ); } else { diff --git a/Event.cpp b/Event.cpp index eb321f8d..3ee6002d 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1018,6 +1018,7 @@ whois_event::run_() { auto *targetcell { static_cast( std::get( target ) ) }; if( targetcell == nullptr ) { continue; } // event effect code + // +40: next station name, unused, stop at next station (duplicate of +0 2nd numeric value) // +32: vehicle name // +24: vehicle type, consist brake level, obstacle distance // +16: load type, load amount, max load amount @@ -1025,6 +1026,35 @@ whois_event::run_() { // +0: train name, station count, stop on next station if( m_input.flags & flags::whois_name ) { // +32 or +40 + // next station name + if( m_input.flags & flags::mode_alt ) { + auto const *owner { ( + ( ( m_activator->Mechanik != nullptr ) && ( m_activator->Mechanik->primary() ) ) ? + m_activator->Mechanik : + m_activator->ctOwner ) }; + auto const nextstop { ( + owner != nullptr ? + owner->TrainTimetable().NextStop() : + "none" ) }; + auto const isstop { ( + ( ( owner != nullptr ) && ( owner->IsStop() ) ) ? + 1 : + 0 ) }; // 1, gdy ma tu zatrzymanie + + targetcell->UpdateValues( + nextstop, // next station name + 0, // unused + isstop, // stop at next station or passthrough + m_input.flags & ( flags::text | flags::value1 | flags::value2 ) ); + + WriteLog( + "Type: WhoIs (" + to_string( m_input.flags ) + ") - " + + "[next station: " + nextstop + "], " + + "[X], " + + "[stop at next station: " + ( isstop != 0 ? "yes" : "no" ) + "]" ); + } + // vehicle name + else { targetcell->UpdateValues( m_activator->asName, // vehicle name 0, // unused @@ -1036,6 +1066,7 @@ whois_event::run_() { + "[name: " + m_activator->asName + "], " + "[X], " + "[X]" ); + } } else if( m_input.flags & flags::whois_load ) { // +16 or +24 @@ -1106,12 +1137,12 @@ whois_event::run_() { m_activator->Mechanik->IsStop() ? 1 : 0, // 1, gdy ma tu zatrzymanie - m_input.flags ); + m_input.flags & ( flags::text | flags::value1 | flags::value2 ) ); WriteLog( "Type: WhoIs (" + to_string( m_input.flags ) + ") - " + "[train: " + m_activator->Mechanik->TrainName() + "], " + "[stations left: " + to_string( m_activator->Mechanik->StationCount() - m_activator->Mechanik->StationIndex() ) + "], " - + "[stop at next: " + ( m_activator->Mechanik->IsStop() ? "yes" : "no") + "]" ); + + "[stop at next station: " + ( m_activator->Mechanik->IsStop() ? "yes" : "no") + "]" ); } } } @@ -2331,6 +2362,8 @@ event_manager::AddToQuery( basic_event *Event, TDynamicObject const *Owner ) { + Event->m_delaydeparture; } } + // NOTE: sanity check, as departure-based delay math can potentially produce negative overall delay + Event->m_launchtime = std::max( Event->m_launchtime, 0.0 ); if( QueryRootEvent != nullptr ) { basic_event *target { QueryRootEvent }; basic_event *previous { nullptr }; diff --git a/Globals.cpp b/Globals.cpp index e2bb3231..1e0bf3eb 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -1014,6 +1014,23 @@ global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token Parser >> gfx_distance_factor_max; gfx_distance_factor_max = clamp(gfx_distance_factor_max, 1.f, 3.f); } + else if (Token == "gfx.shadow.angle.min") + { + Parser.getTokens(1); + Parser >> gfx_shadow_angle_min; + // variable internally uses negative values, but is presented as positive in settings + // so it's likely it'll be supplied as positive number by external launcher + if( gfx_shadow_angle_min > 0 ) { + gfx_shadow_angle_min *= -1; + } + gfx_shadow_angle_min = clamp(gfx_shadow_angle_min, -1.f, -0.2f); + } + else if (Token == "gfx.shadow.rank.cutoff") + { + Parser.getTokens(1); + Parser >> gfx_shadow_rank_cutoff; + gfx_shadow_rank_cutoff = clamp(gfx_shadow_rank_cutoff, 1, 3); + } else { tokenparsed = false; @@ -1236,6 +1253,8 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "gfx.extraeffects", gfx_extraeffects ); export_as_text( Output, "gfx.shadergamma", gfx_shadergamma ); export_as_text( Output, "gfx.drawrange.factor.max", gfx_distance_factor_max ); + export_as_text( Output, "gfx.shadow.angle.min", gfx_shadow_angle_min ); + export_as_text( Output, "gfx.shadow.rank.cutoff", gfx_shadow_rank_cutoff ); export_as_text( Output, "python.enabled", python_enabled ); export_as_text( Output, "python.threadedupload", python_threadedupload ); export_as_text( Output, "python.uploadmain", python_uploadmain ); diff --git a/Globals.h b/Globals.h index cec2e220..c71336b6 100644 --- a/Globals.h +++ b/Globals.h @@ -229,6 +229,8 @@ struct global_settings { bool gfx_shadergamma = false; bool gfx_usegles = false; float gfx_distance_factor_max { 3.f }; + float gfx_shadow_angle_min { -0.2f }; + int gfx_shadow_rank_cutoff { 3 }; std::string exec_on_exit; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 7246ab39..895a2c4b 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1364,6 +1364,7 @@ public: std::array Neighbours; // potential collision sources bool Power110vIsAvailable = false; // cached availability of 110v power bool Power24vIsAvailable = false; // cached availability of 110v power + double Power24vVoltage { 0.0 }; // cached battery voltage bool EventFlag = false; /*!o true jesli cos nietypowego sie wydarzy*/ int SoundFlag = 0; /*!o patrz stale sound_ */ int AIFlag{ 0 }; // HACK: events of interest for consist owner @@ -1489,6 +1490,7 @@ public: bool Mains = false; /*polozenie glownego wylacznika*/ double MainsInitTime{ 0.0 }; // config, initialization time (in seconds) of the main circuit after it receives power, before it can be closed double MainsInitTimeCountdown{ 0.0 }; // current state of main circuit initialization, remaining time (in seconds) until it's ready + start_t MainsStart { start_t::manual }; bool LineBreakerClosesOnlyAtNoPowerPos{ false }; bool ControlPressureSwitch{ false }; // activates if the main pipe and/or brake cylinder pressure aren't within operational levels bool HasControlPressureSwitch{ true }; @@ -1811,6 +1813,7 @@ public: bool ChangeCompressorPreset( int const Change, range_t const Notify = range_t::consist ); bool HeatingSwitch( bool const State, range_t const Notify = range_t::consist ); void HeatingSwitch_( bool const State ); + double EnginePowerSourceVoltage() const; // returns voltage of defined main engine power source /*-funkcje typowe dla lokomotywy elektrycznej*/ void LowVoltagePowerCheck( double const Deltatime ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 7dc41f8b..3d6a6a69 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -171,6 +171,10 @@ double TMoverParameters::Current(double n, double U) } else { Mn = RList[ MainCtrlActualPos ].Mn * RList[ MainCtrlActualPos ].Bn; + if( RList[ MainCtrlActualPos ].Bn > 1 ) { + Bn = 1; + R = CircuitRes; + } } if (DynamicBrakeFlag && (!FuseFlag) && (DynamicBrakeType == dbrake_automatic) && @@ -1511,13 +1515,14 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { PowerCouplersCheck( Deltatime, coupling::power110v ); PowerCouplersCheck( Deltatime, coupling::power24v ); - Power24vIsAvailable = ( ( PowerCircuits[ 0 ].first > 0 ) || ( GetTrainsetVoltage( coupling::power24v ) > 0 ) ); + Power24vVoltage = std::max( PowerCircuits[ 0 ].first, GetTrainsetVoltage( coupling::power24v ) ); + Power24vIsAvailable = ( Power24vVoltage > 0 ); Power110vIsAvailable = ( ( PowerCircuits[ 1 ].first > 0 ) || ( GetTrainsetVoltage( coupling::power110v ) > 0 ) ); } void TMoverParameters::MainsCheck( double const Deltatime ) { - if( MainsInitTime == 0.0 ) { return; } +// if( MainsInitTime == 0.0 ) { return; } // TBD, TODO: move voltage calculation to separate method and use also in power coupler state calculation? auto localvoltage { 0.0 }; @@ -1529,6 +1534,13 @@ void TMoverParameters::MainsCheck( double const Deltatime ) { PantographVoltage ); break; } + case TPowerSource::Accumulator: { + localvoltage = + std::max( + localvoltage, + Power24vVoltage ); + break; + } default: { break; } @@ -1544,6 +1556,13 @@ void TMoverParameters::MainsCheck( double const Deltatime ) { // this allows for simpler rejection of cases where MainsInitTime == 0 MainsInitTimeCountdown -= Deltatime; } + else { + // optional automatic circuit start + if( ( MainsStart != start_t::manual ) + && ( false == ( Mains || dizel_startup ) ) ) { + MainSwitch( true ); + } + } } else { // no power supply @@ -2039,9 +2058,11 @@ void TMoverParameters::MotorBlowersCheck( double const Timestep ) { // activation check for( auto &blower : MotorBlowers ) { auto disable = blower.is_disabled; + auto const start { ( Vel >= blower.min_start_velocity && Im > 0.5 ) }; + auto const stop { ( Vel < 0.5 && Im < 0.5 ) }; if (blower.min_start_velocity >= 0) { - if ( Vel < 0.5 && Im < 0.5 ) + if ( stop ) { blower.stop_timer += Timestep; if (blower.stop_timer > blower.sustain_time) @@ -2049,7 +2070,7 @@ void TMoverParameters::MotorBlowersCheck( double const Timestep ) { disable = true; } } - else if (Vel >= blower.min_start_velocity && Im > 0.5) + else if ( start ) { blower.stop_timer = 0; } @@ -2067,9 +2088,10 @@ void TMoverParameters::MotorBlowersCheck( double const Timestep ) { // && ( true == blower.breaker ) && ( false == disable) && ( ( true == blower.is_active ) - || ( blower.start_type == start_t::manual ? + || ( ( blower.stop_timer == 0.f ) // HACK: will be true for blower with exceeded start_velocity, and for one without start_velocity + && ( blower.start_type == start_t::manual ? blower.is_enabled : - true ) ) ); + true ) ) ) ); } // update for( auto &fan : MotorBlowers ) { @@ -3570,7 +3592,8 @@ bool TMoverParameters::MainSwitchCheck() const { } case TEngineType::ElectricSeriesMotor: case TEngineType::ElectricInductionMotor: { - powerisavailable = ( std::max( GetTrainsetHighVoltage(), PantographVoltage ) > 0.5 * EnginePowerSource.MaxVoltage ); + // TODO: check whether we can simplify this check and skip the outer EngineType switch + powerisavailable = ( EnginePowerSourceVoltage() > 0.5 * EnginePowerSource.MaxVoltage ); break; } default: { @@ -3692,6 +3715,15 @@ void TMoverParameters::HeatingSwitch_( bool const State ) { HeatingAllow = State; } +// returns voltage of defined main engine power source +double TMoverParameters::EnginePowerSourceVoltage() const { + + return ( + EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? std::max( GetTrainsetHighVoltage(), PantographVoltage ) : + EnginePowerSource.SourceType == TPowerSource::Accumulator ? Power24vVoltage : + 0.0 ); +} + // ************************************************************************************************* // Q: 20160711 // zwiększenie nastawy hamulca @@ -4892,10 +4924,8 @@ void TMoverParameters::ComputeTotalForce(double dt) { { // Ra 2014-03: uwzględnienie kierunku jazdy w napięciu na silnikach, a powinien być zdefiniowany nawrotnik EngineVoltage = ( Mains ? - std::max( - GetTrainsetHighVoltage(), - PantographVoltage ) : - 0.00 ); + EnginePowerSourceVoltage() : + 0.0 ); if( CabActive == 0 ) { EngineVoltage *= DirActive; } @@ -10360,6 +10390,13 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { // main circuit extract_value( MainsInitTime, "MainInitTime", line, "" ); + { + auto lookup = starts.find( extract_value( "MainStart", line ) ); + MainsStart = + lookup != starts.end() ? + lookup->second : + start_t::manual; + } // battery { auto lookup = starts.find( extract_value( "BatteryStart", line ) ); diff --git a/Spring.cpp b/Spring.cpp index 587be4f7..da955280 100644 --- a/Spring.cpp +++ b/Spring.cpp @@ -13,6 +13,8 @@ http://mozilla.org/MPL/2.0/. void TSpring::Init(double nKs, double nKd) { Ks = nKs; Kd = nKd; + ks = Ks; + kd = Kd; } Math3D::vector3 TSpring::ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2) { diff --git a/Spring.h b/Spring.h index 30fd0a1b..761bff53 100644 --- a/Spring.h +++ b/Spring.h @@ -29,11 +29,13 @@ public: // double nrestLen= -1.0f); void Init(double nKs = 0.5f, double nKd = 0.002f); Math3D::vector3 ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2); -private: +//private: // members double restLen { 0.01 }; // LENGTH OF SPRING AT REST double Ks { 0.0 }; // SPRING CONSTANT double Kd { 0.0 }; // SPRING DAMPING + float ks{ 0.f }; + float kd{ 0.f }; }; //--------------------------------------------------------------------------- diff --git a/TractionPower.cpp b/TractionPower.cpp index 244151a4..00b17635 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -74,6 +74,9 @@ bool TTractionPowerSource::Load(cParser *parser) { bool TTractionPowerSource::Update(double dt) { // powinno być wykonane raz na krok fizyki // iloczyn napięcia i admitancji daje prąd + if( FastFuse || SlowFuse ) { + TotalCurrent = 0.0; + } if (TotalCurrent > MaxOutputCurrent) { FastFuse = true; diff --git a/Train.cpp b/Train.cpp index 697715f2..d662c574 100644 --- a/Train.cpp +++ b/Train.cpp @@ -758,7 +758,6 @@ dictionary_source *TTrain::GetTrainState( dictionary_source const &Extraparamete dict->insert( "actualproximitydist", driver->ActualProximityDist ); // train data driver->TrainTimetable().serialize( dict ); - dict->insert( "train_stationstart", driver->iStationStart ); dict->insert( "train_atpassengerstop", driver->IsAtPassengerStop ); dict->insert( "train_length", driver->fLength ); // world state data diff --git a/application.cpp b/application.cpp index e734a3a5..378f69cd 100644 --- a/application.cpp +++ b/application.cpp @@ -329,9 +329,10 @@ eu07_application::run() { if (m_network) m_network->update(); - auto frametime = Timer::subsystem.mainloop_total.stop(); - if (Global.minframetime.count() != 0.0f && (Global.minframetime - frametime).count() > 0.0f) - std::this_thread::sleep_for(Global.minframetime - frametime); + auto const frametime{ Timer::subsystem.mainloop_total.stop() }; + if( Global.minframetime.count() != 0.0f && ( Global.minframetime - frametime ).count() > 0.0f ) { + std::this_thread::sleep_for( Global.minframetime - frametime ); + } } return 0; diff --git a/audiorenderer.cpp b/audiorenderer.cpp index 4a729d6e..b6854e6f 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -95,15 +95,29 @@ openal_source::update( double const Deltatime, glm::vec3 const &Listenervelocity sound_change = false; ::alGetSourcei( id, AL_BUFFERS_PROCESSED, &sound_index ); - // for multipart sounds trim away processed sources until only one remains, the last one may be set to looping by the controller + // for multipart sounds trim away processed buffers until only one remains, the last one may be set to looping by the controller // TBD, TODO: instead of change flag move processed buffer ids to separate queue, for accurate tracking of longer buffer sequences - ALuint bufferid; + ALuint discard; while( ( sound_index > 0 ) && ( sounds.size() > 1 ) ) { - ::alSourceUnqueueBuffers( id, 1, &bufferid ); + ::alSourceUnqueueBuffers( id, 1, &discard ); sounds.erase( std::begin( sounds ) ); --sound_index; sound_change = true; + // potentially adjust starting point of the last buffer (to reduce chance of reverb effect with multiple, looping copies playing) + if( ( controller->start() > 0.f ) && ( sounds.size() == 1 ) ) { + ALint bufferid; + ::alGetSourcei( + id, + AL_BUFFER, + &bufferid ); + ALint buffersize; + ::alGetBufferi( bufferid, AL_SIZE, &buffersize ); + ::alSourcei( + id, + AL_SAMPLE_OFFSET, + static_cast( controller->start() * ( buffersize / sizeof( std::int16_t ) ) ) ); + } } int state; diff --git a/audiorenderer.h b/audiorenderer.h index 036ee7d3..580792b5 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -197,7 +197,9 @@ openal_source::bind( sound_source *Controller, uint32_sequence Sounds, Iterator_ ::alSourceQueueBuffers( id, static_cast( buffers.size() ), buffers.data() ); ::alSourceRewind( id ); // sound controller can potentially request playback to start from certain buffer point - if( controller->start() == 0.f ) { + // for multipart sounds the offset is applied only to last piece during playback + // for single sound we also make sure not to apply the offset to optional bookends + if( controller->start() == 0.f || is_multipart || controller->is_bookend( buffers.front() ) ) { // regular case with no offset, reset bound source just in case ::alSourcei( id, AL_SAMPLE_OFFSET, 0 ); } diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 10ac02e4..f4b0d092 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -359,12 +359,12 @@ timetable_panel::update() { if( is_expanded ) { - if( vehicle->MoverParameters->CategoryFlag == 1 ) { + if( owner->is_train() ) { // consist data auto consistmass { owner->fMass }; auto consistlength { owner->fLength }; - if( ( owner->mvControlling->TrainType != dt_DMU ) - && ( owner->mvControlling->TrainType != dt_EZT ) ) { + if( ( false == owner->is_dmu() ) + && ( false == owner->is_emu() ) ) { //odejmij lokomotywy czynne, a przynajmniej aktualną consistmass -= owner->pVehicle->MoverParameters->TotalMass; // subtract potential other half of a two-part vehicle @@ -392,12 +392,12 @@ timetable_panel::update() { m_tablelines.emplace_back( u8"┌─────┬────────────────────────────────────┬─────────┬─────┐", Global.UITextColor ); TMTableLine const *tableline; - for( int i = owner->iStationStart; i <= table.StationCount; ++i ) { + for( int i = table.StationStart; i <= table.StationCount; ++i ) { // wyświetlenie pozycji z rozkładu tableline = table.TimeTable + i; // linijka rozkładu bool vmaxchange { true }; - if( i > owner->iStationStart ) { + if( i > table.StationStart ) { auto const *previoustableline { tableline - 1 }; if( tableline->vmax == previoustableline->vmax ) { vmaxchange = false; @@ -427,7 +427,7 @@ timetable_panel::update() { to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Dm, true, 3 ) : u8" │ " ) }; auto const candepart { ( - ( owner->iStationStart < table.StationIndex ) + ( table.StationStart < table.StationIndex ) && ( i < table.StationIndex ) && ( ( tableline->Ah < 0 ) // pass-through, always valid || ( tableline->is_maintenance ) // maintenance stop, always valid @@ -439,7 +439,7 @@ timetable_panel::update() { tableline->Ah >= 0 ? to_minutes_str( CompareTime( table.TimeTable[ i - 1 ].Dh, table.TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), false, 3 ) : to_minutes_str( std::max( 0.0, CompareTime( table.TimeTable[ i - 1 ].Dh, table.TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), false, 3 ) ) }; auto const linecolor { ( - ( i != owner->iStationStart ) ? Global.UITextColor : + ( i != table.StationStart ) ? Global.UITextColor : loadchangeinprogress ? colors::uitextred : candepart ? colors::uitextgreen : // czas minął i odjazd był, to nazwa stacji będzie na zielono isatpassengerstop ? colors::uitextorange : @@ -1425,6 +1425,10 @@ debug_panel::render_section_settings() { ImGui::PopStyleColor(); // reflection fidelity ImGui::SliderInt( ( to_string( Global.reflectiontune.fidelity ) + "###reflectionfidelity" ).c_str(), &Global.reflectiontune.fidelity, 0, 2, "Reflection fidelity" ); + ImGui::SliderInt( ( to_string( Global.gfx_shadow_rank_cutoff ) + "###shadowrankcutoff" ).c_str(), &Global.gfx_shadow_rank_cutoff, 1, 3, "Shadow ranks" ); + if( ImGui::SliderFloat( ( to_string( std::abs( Global.gfx_shadow_angle_min ), 2 ) + "###shadowanglecutoff" ).c_str(), &Global.gfx_shadow_angle_min, -1.0, -0.2, "Shadow angle cutoff" ) ) { + Global.gfx_shadow_angle_min = quantize( Global.gfx_shadow_angle_min, 0.05f ); + }; if( DebugModeFlag ) { // sky sliders { diff --git a/material.cpp b/material.cpp index 06a9634f..e3a6a7c0 100644 --- a/material.cpp +++ b/material.cpp @@ -391,6 +391,11 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c glossiness = std::stof(value); //m7t: handle exception m_glossiness_priority = Priority; } + else if (key == "shadow_rank:") + { + auto const value { deserialize_random_set( Input ) }; + shadow_rank = std::stof(value); //m7t: handle exception + } else if( key == "size:" ) { Input.getTokens( 2 ); Input diff --git a/material.h b/material.h index 7b77d26f..c14d9c0e 100644 --- a/material.h +++ b/material.h @@ -27,6 +27,7 @@ struct opengl_material { std::optional opacity; std::optional selfillum; float glossiness { 10.f }; + int shadow_rank { 0 }; // priority as shadow caster; higher = more likely to be skipped std::string name; glm::vec2 size { -1.f, -1.f }; // 'physical' size of bound texture, in meters diff --git a/mtable.cpp b/mtable.cpp index 09d04abd..54279d23 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -184,10 +184,10 @@ bool TTrainParameters::IsTimeToGo(double hh, double mm) // returns: difference between specified time and scheduled departure from current stop, in seconds double TTrainParameters::seconds_until_departure( double const Hour, double const Minute ) const { - if( ( TimeTable[ StationIndex ].Ah < 0 ) ) { // passthrough + if( ( TimeTable[ StationStart ].Ah < 0 ) ) { // passthrough return 0; } - return ( 60.0 * CompareTime( Hour, Minute, TimeTable[ StationIndex ].Dh, TimeTable[ StationIndex ].Dm ) ); + return ( 60.0 * CompareTime( Hour, Minute, TimeTable[ StationStart ].Dh, TimeTable[ StationStart ].Dm ) ); } std::string TTrainParameters::ShowRelation() const @@ -212,6 +212,7 @@ void TTrainParameters::NewName(std::string const &NewTrainName) TrainName = NewTrainName; StationCount = 0; StationIndex = 0; + StationStart = 0; NextStationName = "nowhere"; LastStationLatency = 0; Direction = 1; @@ -649,6 +650,7 @@ void TTrainParameters::serialize( dictionary_source *Output ) const { Output->insert( "train_stationto", Relation2 ); Output->insert( "train_stationindex", StationIndex ); Output->insert( "train_stationcount", StationCount ); + Output->insert( "train_stationstart", StationStart ); if( StationCount > 0 ) { // timetable stations data, if there's any for( auto stationidx = 1; stationidx <= StationCount; ++stationidx ) { diff --git a/mtable.h b/mtable.h index d5aea177..15b66940 100644 --- a/mtable.h +++ b/mtable.h @@ -61,6 +61,7 @@ class TTrainParameters TMTable TimeTable; int StationCount; // ilość przystanków (0-techniczny) int StationIndex; // numer najbliższego (aktualnego) przystanku + int StationStart; // numer pierwszej stacji pokazywanej na podglądzie rozkładu std::string NextStationName; double LastStationLatency; int Direction; /*kierunek jazdy w/g kilometrazu*/ diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index e2608334..51c7ff46 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -1289,7 +1289,7 @@ void opengl33_renderer::setup_pass(viewport_config &Viewport, renderpass_config bounding_box(frustumchunkmin, frustumchunkmax, std::begin(frustumchunkshapepoints), std::end(frustumchunkshapepoints)); auto const frustumchunkcentre = (frustumchunkmin + frustumchunkmax) * 0.5f; // ...cap the vertical angle to keep shadows from getting too long... - auto const lightvector = glm::normalize(glm::vec3{m_sunlight.direction.x, std::min(m_sunlight.direction.y, -0.2f), m_sunlight.direction.z}); + auto const lightvector = glm::normalize(glm::vec3{m_sunlight.direction.x, std::min(m_sunlight.direction.y, Global.gfx_shadow_angle_min), m_sunlight.direction.z}); // ...place the light source at the calculated centre and setup world space light view matrix... camera.position() = worldview.pass_camera.position() + glm::dvec3{frustumchunkcentre}; viewmatrix *= glm::lookAt(camera.position(), camera.position() + glm::dvec3{lightvector}, glm::dvec3{0.f, 1.f, 0.f}); @@ -1974,6 +1974,12 @@ opengl_material &opengl33_renderer::Material(material_handle const Material) return m_materials.material(Material); } +opengl_material const & opengl33_renderer::Material( TSubModel const * Submodel ) const { + + auto const material { Submodel->m_material >= 0 ? Submodel->m_material : Submodel->ReplacableSkinId[ -Submodel->m_material ] }; + return Material( material ); +} + texture_handle opengl33_renderer::Fetch_Texture(std::string const &Filename, bool const Loadnow, GLint format_hint) { return m_textures.create(Filename, Loadnow, format_hint); @@ -2001,11 +2007,6 @@ opengl_texture const &opengl33_renderer::Texture(texture_handle const Texture) c return m_textures.texture(Texture); } -void opengl33_renderer::Update_AnimModel(TAnimModel *model) -{ - model->RaAnimate(m_framestamp); -} - void opengl33_renderer::Render(scene::basic_region *Region) { @@ -2442,18 +2443,26 @@ void opengl33_renderer::Render(scene::shape_node const &Shape, bool const Ignore switch (m_renderpass.draw_mode) { case rendermode::color: - case rendermode::reflections: - Bind_Material(data.material, nullptr, &Shape.data().lighting ); - break; - case rendermode::shadows: - Bind_Material_Shadow(data.material); - break; + case rendermode::reflections: { + Bind_Material( data.material, nullptr, &Shape.data().lighting ); + break; + } + case rendermode::shadows: { + // skip if the shadow caster rank is too low for currently set threshold + if( Material( data.material ).shadow_rank > Global.gfx_shadow_rank_cutoff ) { + return; + } + Bind_Material_Shadow( data.material ); + break; + } case rendermode::pickscenery: - case rendermode::pickcontrols: - m_pick_shader->bind(); - break; - default: - break; + case rendermode::pickcontrols: { + m_pick_shader->bind(); + break; + } + default: { + break; + } } // render @@ -2921,6 +2930,13 @@ void opengl33_renderer::Render(TSubModel *Submodel) } case rendermode::shadows: { + // skip if the shadow caster rank is too low for currently set threshold + if( Material( Submodel ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + --m_renderpass.draw_stats.submodels; + --m_renderpass.draw_stats.drawcalls; + break; + } if (Submodel->m_material < 0) { // zmienialne skóry Bind_Material_Shadow(Submodel->ReplacableSkinId[-Submodel->m_material]); @@ -3098,6 +3114,10 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator // shadows are only calculated for high enough roads, typically meaning track platforms continue; } + if( Material( track->m_material1 ).shadow_rank > Global.gfx_shadow_rank_cutoff ) { + // skip if the shadow caster rank is too low for currently set threshold + continue; + } Bind_Material_Shadow(track->m_material1); draw(std::begin(track->Geometry1), std::end(track->Geometry1)); break; @@ -3149,7 +3169,11 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator // shadows are only calculated for high enough trackbeds continue; } - Bind_Material_Shadow(track->m_material2); + if( Material( track->m_material2 ).shadow_rank > Global.gfx_shadow_rank_cutoff ) { + // skip if the shadow caster rank is too low for currently set threshold + continue; + } + Bind_Material_Shadow(track->m_material2); draw(std::begin(track->Geometry2), std::end(track->Geometry2)); break; } @@ -3206,6 +3230,11 @@ void opengl33_renderer::Render(scene::basic_cell::path_sequence::const_iterator // shadows are only calculated for high enough trackbeds continue; } + if( Material( track->SwitchExtension->m_material3 ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + // skip if the shadow caster rank is too low for currently set threshold + continue; + } Bind_Material_Shadow(track->SwitchExtension->m_material3); draw(track->SwitchExtension->Geometry3); break; @@ -3739,6 +3768,13 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) } case rendermode::shadows: { + // skip if the shadow caster rank is too low for currently set threshold + if( Material( Submodel ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + --m_renderpass.draw_stats.submodels; + --m_renderpass.draw_stats.drawcalls; + break; + } if (Submodel->m_material < 0) { // zmienialne skóry Bind_Material_Shadow(Submodel->ReplacableSkinId[-Submodel->m_material]); diff --git a/opengl33renderer.h b/opengl33renderer.h index e282545e..76f95985 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -123,15 +123,13 @@ class opengl33_renderer : public gfx_renderer { std::string const & info_stats() const override; - - opengl_material & Material( material_handle const Material ); + opengl_material const & Material( TSubModel const * Submodel ) const; // draws supplied geometry handles void Draw_Geometry(std::vector::iterator begin, std::vector::iterator end); void Draw_Geometry(const gfx::geometrybank_handle &handle); // material methods void Bind_Material_Shadow(material_handle const Material); - void Update_AnimModel(TAnimModel *model); // members GLenum static const sunlight{0}; diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 57f3d217..1e19a63f 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -972,7 +972,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f auto const lightvector = glm::normalize( glm::vec3{ m_sunlight.direction.x, - std::min( m_sunlight.direction.y, -0.2f ), + std::min( m_sunlight.direction.y, Global.gfx_shadow_angle_min ), m_sunlight.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 }; @@ -1024,7 +1024,7 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f auto const lightvector = glm::normalize( glm::vec3{ m_sunlight.direction.x, - std::min( m_sunlight.direction.y, -0.2f ), + std::min( m_sunlight.direction.y, Global.gfx_shadow_angle_min ), m_sunlight.direction.z } ); camera.position() = Global.pCamera.Pos - glm::dvec3 { lightvector }; viewmatrix *= glm::lookAt( @@ -1757,6 +1757,13 @@ opengl_renderer::Material( material_handle const Material ) const { return m_materials.material( Material ); } +opengl_material const & +opengl_renderer::Material( TSubModel const * Submodel ) const { + + auto const material { Submodel->m_material >= 0 ? Submodel->m_material : Submodel->ReplacableSkinId[ -Submodel->m_material ] }; + return Material( material ); +} + // shader methods std::shared_ptr opengl_renderer::Fetch_Shader( std::string const &name ) { @@ -2285,7 +2292,13 @@ opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange break; } // pick modes are painted with custom colours, and shadow pass doesn't use any - case rendermode::shadows: + case rendermode::shadows: { + // skip if the shadow caster rank is too low for currently set threshold + if( Material( data.material ).shadow_rank > Global.gfx_shadow_rank_cutoff ) { + return; + } + } + [[ fallthrough ]]; case rendermode::cabshadows: case rendermode::pickscenery: case rendermode::pickcontrols: @@ -2815,7 +2828,16 @@ opengl_renderer::Render( TSubModel *Submodel ) { #endif break; } - case rendermode::shadows: + case rendermode::shadows: { + // skip if the shadow caster rank is too low for currently set threshold + if( Material( Submodel ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + --m_renderpass.draw_stats.submodels; + --m_renderpass.draw_stats.drawcalls; + break; + } + } + [[fallthrough]]; case rendermode::cabshadows: case rendermode::pickscenery: { // scenery picking and shadow both use enforced colour and no frills @@ -3102,6 +3124,11 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, // shadows are only calculated for high enough roads, typically meaning track platforms continue; } + if( Material( track->m_material1 ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + // skip if the shadow caster rank is too low for currently set threshold + continue; + } Bind_Material( track->m_material1 ); m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); break; @@ -3147,6 +3174,11 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, // shadows are only calculated for high enough trackbeds continue; } + if( Material( track->m_material2 ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + // skip if the shadow caster rank is too low for currently set threshold + continue; + } Bind_Material( track->m_material2 ); m_geometry.draw( std::begin( track->Geometry2 ), std::end( track->Geometry2 ) ); break; @@ -3195,6 +3227,11 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, // shadows are only calculated for high enough trackbeds continue; } + if( Material( track->SwitchExtension->m_material3 ).shadow_rank > Global.gfx_shadow_rank_cutoff ) + { + // skip if the shadow caster rank is too low for currently set threshold + continue; + } Bind_Material( track->SwitchExtension->m_material3 ); m_geometry.draw( track->SwitchExtension->Geometry3 ); break; diff --git a/openglrenderer.h b/openglrenderer.h index ffc2f8cf..a18b3c85 100644 --- a/openglrenderer.h +++ b/openglrenderer.h @@ -117,6 +117,8 @@ public: std::string const & info_stats() const override; + opengl_material const & Material( TSubModel const * Submodel ) const; + // members GLenum static const sunlight { GL_LIGHT0 }; diff --git a/particles.cpp b/particles.cpp index 9c45717f..13197dda 100644 --- a/particles.cpp +++ b/particles.cpp @@ -301,7 +301,7 @@ smoke_source::initialize( smoke_particle &Particle ) { if( m_ownertype == owner_type::vehicle ) { Particle.opacity *= m_owner.vehicle->MoverParameters->dizel_fill; - auto const enginerevolutionsfactor { 1.5f }; // high engine revolutions increase initial particle velocity + auto const enginerevolutionsfactor { 0.5f }; // high engine revolutions increase initial particle velocity switch( m_owner.vehicle->MoverParameters->EngineType ) { case TEngineType::DieselElectric: { Particle.velocity *= 1.0 + enginerevolutionsfactor * m_owner.vehicle->MoverParameters->enrot / ( m_owner.vehicle->MoverParameters->DElist[ m_owner.vehicle->MoverParameters->MainCtrlPosNo ].RPM / 60.0 ); @@ -331,7 +331,7 @@ smoke_source::update( smoke_particle &Particle, bounding_box &Boundingbox, doubl // crude smoke dispersion simulation // http://www.auburn.edu/academic/forestry_wildlife/fire/smoke_guide/smoke_dispersion.htm Particle.velocity.y += ( 0.005 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 10 ) * Timedelta; // decelerate faster in cold weather - Particle.velocity.y -= ( 0.010 * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation + Particle.velocity.y -= ( 0.050 * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation Particle.velocity.y = std::max( 0.25 * ( 2.f - Global.Overcast ), Particle.velocity.y ); // put a cap on deceleration Particle.position += Particle.velocity * static_cast( Timedelta ); diff --git a/sound.cpp b/sound.cpp index 62123844..e20938aa 100644 --- a/sound.cpp +++ b/sound.cpp @@ -182,6 +182,11 @@ sound_source::deserialize_mapping( cParser &Input ) { >> soundproofing[ 3 ] >> soundproofing[ 4 ] >> soundproofing[ 5 ]; + for( auto & soundproofingelement : soundproofing ) { + if( soundproofingelement != -1.f ) { + soundproofingelement = std::sqrtf( clamp( soundproofingelement, 0.f, 1.f ) ); + } + } m_soundproofing = soundproofing; } else if( starts_with( key, "sound" ) ) { @@ -892,6 +897,20 @@ sound_source::is_combined() const { return ( ( !m_soundchunks.empty() ) && ( sound( sound_id::main ).buffer == null_handle ) ); } +// returns true if specified buffer is one of the optional bookends +bool +sound_source::is_bookend( audio::buffer_handle const Buffer ) const { + + return ( ( sound( sound_id::begin ).buffer == Buffer ) || ( sound( sound_id::end ).buffer == Buffer ) ); +} + +// returns true if the source has optional bookends +bool +sound_source::has_bookends() const { + + return ( ( sound( sound_id::begin ).buffer != null_handle ) && ( sound( sound_id::end ).buffer != null_handle ) ); +} + // returns location of the sound source in simulation region space glm::dvec3 const sound_source::location() const { diff --git a/sound.h b/sound.h index 944cd600..eb09b589 100644 --- a/sound.h +++ b/sound.h @@ -130,6 +130,12 @@ public: // returns true if the source uses sample table bool is_combined() const; + // returns true if specified buffer is one of the optional bookends + bool + is_bookend( audio::buffer_handle const Buffer ) const; + // returns true if the source has optional bookends + bool + has_bookends() const; // returns location of the sound source in simulation region space glm::dvec3 const location() const; diff --git a/version.h b/version.h index ae0dccc8..1667a188 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 21 -#define VERSION_MINOR 509 +#define VERSION_MINOR 905 #define VERSION_REVISION 0