diff --git a/Classes.h b/Classes.h index ecbfd1b4..8c4209cc 100644 --- a/Classes.h +++ b/Classes.h @@ -40,6 +40,7 @@ class powergridsource_table; class instance_table; class vehicle_table; struct light_array; +struct dictionary_source; namespace scene { struct node_data; diff --git a/Driver.cpp b/Driver.cpp index ee3ff37c..b847cd23 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1375,7 +1375,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( ( mvOccupied->Vel < v ) || ( v == 0.0 ) ) { // if we're going slower than the target velocity and there's enough room for safe stop, speed up - auto const brakingdistance = fBrakeDist * braking_distance_multiplier( v ); + auto const brakingdistance { 1.2 * fBrakeDist * braking_distance_multiplier( v ) }; if( brakingdistance > 0.0 ) { // maintain desired acc while we have enough room to brake safely, when close enough start paying attention // try to make a smooth transition instead of sharp change @@ -2127,6 +2127,11 @@ bool TController::CheckVehicles(TOrders user) && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) { // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly p->MoverParameters->BatterySwitch( true ); + // enable heating and converter in carriages with can be heated + if( p->MoverParameters->HeatingPower > 0 ) { + p->MoverParameters->HeatingAllow = true; + p->MoverParameters->ConverterSwitch( true, range_t::local ); + } } if (p->asDestination == "none") @@ -2358,6 +2363,7 @@ double TController::BrakeAccFactor() const double Factor = 1.0; if( ( fAccThreshold != 0.0 ) + && ( AccDesired < 0.0 ) && ( ( ActualProximityDist > fMinProximityDist ) || ( mvOccupied->Vel > VelDesired + fVelPlus ) ) ) { Factor += ( fBrakeReaction * ( /*mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.5 ? 1.5 : 1 ) ) * mvOccupied->Vel / ( std::max( 0.0, ActualProximityDist ) + 1 ) * ( ( AccDesired - AbsAccS_pub ) / fAccThreshold ); @@ -2529,9 +2535,9 @@ bool TController::PrepareEngine() } // sync virtual brake state with the 'real' one std::unordered_map const brakepositions { - { mvOccupied->Handle->GetPos( bh_RP ), gbh_RP }, - { mvOccupied->Handle->GetPos( bh_NP ), gbh_NP }, - { mvOccupied->Handle->GetPos( bh_FS ), gbh_FS } }; + { static_cast( mvOccupied->Handle->GetPos( bh_RP ) ), gbh_RP }, + { static_cast( mvOccupied->Handle->GetPos( bh_NP ) ), gbh_NP }, + { static_cast( mvOccupied->Handle->GetPos( bh_FS ) ), gbh_FS } }; auto const lookup { brakepositions.find( static_cast( mvOccupied->fBrakeCtrlPos ) ) }; if( lookup != brakepositions.end() ) { BrakeLevelSet( lookup->second ); // GBH @@ -3568,7 +3574,7 @@ void TController::Doors( bool const Open, int const Side ) { } if( AIControllFlag ) { - if( ( true == mvOccupied->Doors.has_autowarning ) + if( ( true == mvOccupied->Doors.has_warning ) && ( false == mvOccupied->DepartureSignal ) && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { mvOccupied->signal_departure( true ); // załącenie bzyczka @@ -3581,13 +3587,14 @@ void TController::Doors( bool const Open, int const Side ) { || ( false == AIControllFlag ) ) ) { // ai doesn't close the door until it's free to depart, but human driver has free reign to do stupid things if( ( pVehicle->MoverParameters->Doors.close_control == control_t::conductor ) - || ( ( true == AIControllFlag ) - && ( ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) - || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) ) ) { + || ( ( true == AIControllFlag ) ) ) { // if the door are controlled by the driver, we let the user operate them unless this user is an ai // the train conductor, if present, handles door operation also for human-driven trains - pVehicle->MoverParameters->OperateDoors( side::right, false ); - pVehicle->MoverParameters->OperateDoors( side::left, false ); + if( ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) + || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) { + pVehicle->MoverParameters->OperateDoors( side::right, false ); + pVehicle->MoverParameters->OperateDoors( side::left, false ); + } if( pVehicle->MoverParameters->Doors.permit_needed ) { pVehicle->MoverParameters->PermitDoors( side::right, false ); pVehicle->MoverParameters->PermitDoors( side::left, false ); @@ -4687,11 +4694,11 @@ TController::UpdateSituation(double dt) { // TODO: test if we can use the distances calculation from obey_train fMinProximityDist = std::min( 5 + iVehicles, 25 ); fMaxProximityDist = std::min( 10 + iVehicles, 50 ); -/* - if( IsHeavyCargoTrain ) { - fMaxProximityDist *= 1.5; + // HACK: modern vehicles might brake slower at low speeds, increase safety margin as crude counter + if( mvControlling->EIMCtrlType > 0 ) { + fMinProximityDist += 5.0; + fMaxProximityDist += 5.0; } -*/ fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania // margines prędkości powodujący załączenie napędu // były problemy z jazdą np. 3km/h podczas ładowania wagonów @@ -5171,7 +5178,11 @@ TController::UpdateSituation(double dt) { 20.0 ) ); // others if( vel > VelDesired + fVelPlus ) { // if going too fast force some prompt braking - AccPreferred = std::min( -0.65, AccPreferred ); + AccPreferred = std::min( + ( ( mvOccupied->CategoryFlag & 2 ) ? + -0.65 : // cars + -0.30 ), // others + AccPreferred ); } } @@ -5683,11 +5694,11 @@ TController::UpdateSituation(double dt) { || ( VelNext > vel - 40.0 ) ) ? fBrake_a0[ 0 ] * 0.8 : -fAccThreshold ) - / braking_distance_multiplier( VelNext ) ) { + / ( 1.2 * braking_distance_multiplier( VelNext ) ) ) { AccDesired = std::max( -0.06, AccDesired ); } } - else { + if( AccDesired < -0.1 ) { // i orientuj się szybciej, jeśli hamujesz ReactionTime = 0.25; } @@ -5786,11 +5797,11 @@ TController::UpdateSituation(double dt) { } } } - // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej - // zwiekszanie predkosci + // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej zwiekszanie predkosci // Ra 2F1H: jest konflikt histerezy pomiędzy nastawioną pozycją a uzyskiwanym // przyspieszeniem - utrzymanie pozycji powoduje przekroczenie przyspieszenia - if( ( AccDesired - AbsAccS > 0.01 ) ) { + if( ( AccDesired > -0.06 ) // don't add power if not asked for actual speed-up + && ( AccDesired - AbsAccS > 0.05 ) ) { // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz... if( vel < ( VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1 @@ -5882,7 +5893,8 @@ TController::UpdateSituation(double dt) { } } } - if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - fBrake_a1[0]*0.51)) { + if ( ( AccDesired < fAccGravity - 0.05 ) + && ( ( AccDesired - fBrake_a1[0]*0.51 ) ) - AbsAccS > 0.05 ) { // jak hamuje, to nie tykaj kranu za często // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 if( OrderCurrentGet() != Disconnect ) { diff --git a/Driver.h b/Driver.h index 1c4034d1..d75de7d1 100644 --- a/Driver.h +++ b/Driver.h @@ -407,9 +407,9 @@ private: // methods public: std::string TrainName() const; + Mtable::TTrainParameters const * TrainTimetable() const; private: std::string Relation() const; - Mtable::TTrainParameters const * TrainTimetable() const; int StationIndex() const; int StationCount() const; bool IsStop() const; diff --git a/DynObj.cpp b/DynObj.cpp index b7e14ee3..6c93fbb6 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -160,6 +160,39 @@ void TAnim::Parovoz(){ // animowanie tłoka i rozrządu parowozu }; */ + + +void TDynamicObject::destination_data::deserialize( cParser &Input ) { + + while( true == deserialize_mapping( Input ) ) { + ; // all work done by while() + } +} + +bool TDynamicObject::destination_data::deserialize_mapping( cParser &Input ) { + // token can be a key or block end + auto const key { Input.getToken( true, "\n\r\t ,;[]" ) }; + + if( ( true == key.empty() ) || ( key == "}" ) ) { return false; } + + if( key == "{" ) { + script = Input.getToken(); + } + else if( key == "update:" ) { + auto const value { Input.getToken() }; + // TODO: implement + } + else if( key == "instance:" ) { + instancing = Input.getToken(); + } + else if( key == "parameters:" ) { + parameters = Input.getToken(); + } + + return true; +} + + //--------------------------------------------------------------------------- TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr, int cf) { // szukanie skrajnego połączonego pojazdu w pociagu @@ -919,6 +952,8 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btEndSignalsTab2.Turn( true ); btnOn = true; } + // destination signs + update_destinations(); // else btEndSignalsTab2.TurnOff(); // McZapkie-181002: krecenie wahaczem (korzysta z kata obrotu silnika) if (iAnimType[ANIM_LEVERS]) @@ -1946,6 +1981,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" init_sections( mdLowPolyInt, nameprefix ); } } + // destination sign + if( mdModel ) { + init_destination( mdModel ); + } // 'external_load' is an optional special section in the main model, pointing to submodel of external load if( mdModel ) { init_sections( mdModel, "external_load" ); @@ -2069,6 +2108,16 @@ TDynamicObject::init_sections( TModel3d const *Model, std::string const &Namepre return sectioncount; } +bool +TDynamicObject::init_destination( TModel3d *Model ) { + + if( Model->GetSMRoot() == nullptr ) { return false; } + + std::tie( DestinationSign.sign, DestinationSign.has_light ) = Model->GetSMRoot()->find_replacable4(); + + return DestinationSign.sign != nullptr; +} + void TDynamicObject::create_controller( std::string const Type, bool const Trainset ) { @@ -2553,6 +2602,27 @@ na sprzęgach, opóźnienie działania hamulca itp. Oczywiście musi mieć to pe histerezę czasową, aby te tryby pracy nie przełączały się zbyt szybko. */ +void TDynamicObject::update_destinations() { + + if( DestinationSign.sign == nullptr ) { return; } + + DestinationSign.sign->fLight = ( + ( ( DestinationSign.has_light ) && ( MoverParameters->Battery ) ) ? + 2.0 : + -1.0 ); + + // jak są 4 tekstury wymienne, to nie zmieniać rozkładem + if( std::abs( m_materialdata.multi_textures ) >= 4 ) { return; } + // TODO: dedicated setting to discern electronic signs, instead of fallback on light presence + m_materialdata.replacable_skins[ 4 ] = ( + ( ( DestinationSign.destination != null_handle ) + && ( ( false == DestinationSign.has_light ) // physical destination signs remain up until manually changed + || ( ( true == MoverParameters->Battery ) // lcd signs are off without power + && ( ctOwner != nullptr ) ) ) ) ? // lcd signs are off for carriages without engine, potentially left on a siding + DestinationSign.destination : + DestinationSign.destination_off ); +} + bool TDynamicObject::Update(double dt, double dt1) { if (dt1 == 0) @@ -4017,6 +4087,10 @@ void TDynamicObject::RenderSounds() { } if( volume > 0.05 ) { rscurve + .pitch( + true == rscurve.is_combined() ? + MoverParameters->Vel * 0.01f : + rscurve.m_frequencyoffset + rscurve.m_frequencyfactor * 1.f ) .gain( 2.5 * volume ) .play( sound_flags::exclusive | sound_flags::looping ); } @@ -4241,7 +4315,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } // potentially set blank destination texture - DestinationSet( {}, {} ); + DestinationSign.destination_off = DestinationFind( "nowhere" ); +// DestinationSet( {}, {} ); if( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { // tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych @@ -5537,6 +5612,15 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co parser >> JointCabs; } + else if( token == "pydestinationsign:" ) { + DestinationSign.deserialize( parser ); + // supply vehicle folder as script path if none is provided + if( ( false == DestinationSign.script.empty() ) + && ( substr_path( DestinationSign.script ).empty() ) ) { + DestinationSign.script = asBaseDir + DestinationSign.script; + } + } + } while( token != "" ); } // internaldata: @@ -6093,45 +6177,92 @@ int TDynamicObject::RouteWish(TTrack *tr) return Mechanik ? Mechanik->CrossRoute(tr) : 0; // wg AI albo prosto }; -void TDynamicObject::DestinationSet(std::string to, std::string numer) -{ // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik +void TDynamicObject::DestinationSet(std::string to, std::string numer) { + // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik // w zasadzie, to każdy wagon mógłby mieć inną stację docelową // zwłaszcza w towarowych, pod kątem zautomatyzowania maewrów albo pracy górki // ale to jeszcze potrwa, zanim będzie możliwe, na razie można wpisać stację z // rozkładu - if( std::abs( m_materialdata.multi_textures ) >= 4 ) { - // jak są 4 tekstury wymienne, to nie zmieniać rozkładem - return; - } - numer = Bezogonkow(numer); + asDestination = to; - to = Bezogonkow(to); // do szukania pliku obcinamy ogonki - if( true == to.empty() ) { - to = "nowhere"; + + if( std::abs( m_materialdata.multi_textures ) >= 4 ) { return; } // jak są 4 tekstury wymienne, to nie zmieniać rozkładem + if( DestinationSign.sign == nullptr ) { return; } // no sign submodel, no problem + + // now see if we can find any version of the destination texture + std::vector const destinations = { + numer, // try dedicated timetable sign first... + to }; // ...then generic destination sign + + for( auto const &destination : destinations ) { + + DestinationSign.destination = DestinationFind( destination ); + if( DestinationSign.destination != null_handle ) { + // got what we wanted, we're done here + return; + } } + // if we didn't get static texture we might be able to make one + if( DestinationSign.script.empty() ) { return; } // no script so no way to make the texture + if( numer == "none" ) { return; } // blank or incomplete/malformed timetable, don't bother + + std::string signrequest { + "make:" + + DestinationSign.script + "?" + // timetable include + + "$timetable=" + ( + ctOwner == nullptr ? + MoverParameters->Name : // leading vehicle, can point to it directly + ctOwner->Vehicle()->MoverParameters->Name ) + "&" // owned vehicle, safer to point to owner as carriages can have identical names + // basic instancing string + // NOTE: underscore doesn't have any magic meaning for the time being, it's just less likely to conflict with regular dictionary keys + + "_id1=" + ( + ctOwner != nullptr ? ctOwner->TrainName() : + Mechanik != nullptr ? Mechanik->TrainName() : + "none" ) }; // shouldn't get here but, eh + // TBD, TODO: replace instancing with support for variables in extra parameters string? + if( false == DestinationSign.instancing.empty() ) { + signrequest += + "&_id2=" + ( + DestinationSign.instancing == "name" ? MoverParameters->Name : + DestinationSign.instancing == "type" ? MoverParameters->TypeName : + "none" ); + } + // optionl extra parameters + if( false == DestinationSign.parameters.empty() ) { + signrequest += "&" + DestinationSign.parameters; + } + + DestinationSign.destination = GfxRenderer.Fetch_Material( signrequest ); +} + +material_handle TDynamicObject::DestinationFind( std::string Destination ) { + + if( Destination.empty() ) { return null_handle; } + + Destination = Bezogonkow( Destination ); // do szukania pliku obcinamy ogonki // destination textures are kept in the vehicle's directory so we point the current texture path there auto const currenttexturepath { Global.asCurrentTexturePath }; Global.asCurrentTexturePath = asBaseDir; // now see if we can find any version of the texture - std::vector destinations = { - numer + '@' + MoverParameters->TypeName, - numer, - to + '@' + MoverParameters->TypeName, - to, - "nowhere" + '@' + MoverParameters->TypeName, - "nowhere" }; + std::vector const destinations { + Destination + '@' + MoverParameters->TypeName, + Destination }; + + auto destinationhandle { null_handle }; for( auto const &destination : destinations ) { - auto material = TextureTest( ToLower( destination ) ); if( false == material.empty() ) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( material ); + destinationhandle = GfxRenderer.Fetch_Material( material ); break; } } // whether we got anything, restore previous texture path Global.asCurrentTexturePath = currenttexturepath; -}; + + return destinationhandle; +} void TDynamicObject::OverheadTrack(float o) { // ewentualne wymuszanie jazdy diff --git a/DynObj.h b/DynObj.h index f683cce9..1bbe000a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -200,6 +200,20 @@ public: TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly std::array LowPolyIntCabs {}; // pointers to low fidelity version of individual driver cabs bool JointCabs{ false }; // flag for vehicles with multiple virtual 'cabs' sharing location and 3d model(s) + struct destination_data { + TSubModel *sign { nullptr }; // submodel mapped with replacable texture -4 + bool has_light { false }; // the submodel was originally configured with self-illumination attribute + material_handle destination { null_handle }; // most recently assigned non-blank destination texture + material_handle destination_off { null_handle }; // blank destination sign + std::string script; // potential python script used to generate texture data + int update_rate { 0 }; // -1: per stop, 0: none, >0: fps // TBD, TODO: implement? + std::string instancing; // potential method to generate more than one texture per timetable + std::string parameters; // potential extra parameters supplied by mmd file + // methods + void deserialize( cParser &Input ); + private: + bool deserialize_mapping( cParser &Input ); + } DestinationSign; float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) float LoadOffset { 0.f }; std::unordered_map LoadModelOverrides; // potential overrides of default load visualization models @@ -517,6 +531,7 @@ private: TTrack *Track, double fDist, std::string DriverType, double fVel, std::string TrainName, float Load, std::string LoadType, bool Reversed, std::string); int init_sections( TModel3d const *Model, std::string const &Nameprefix ); + bool init_destination( TModel3d *Model ); void create_controller( std::string const Type, bool const Trainset ); void AttachPrev(TDynamicObject *Object, int iType = 1); bool UpdateForce(double dt); @@ -531,6 +546,7 @@ private: void update_load_visibility(); void update_load_offset(); void shuffle_load_sections(); + void update_destinations(); bool Update(double dt, double dt1); bool FastUpdate(double dt); void Move(double fDistance); @@ -633,6 +649,7 @@ private: // zapytanie do AI, po którym segmencie skrzyżowania jechać int RouteWish(TTrack *tr); void DestinationSet(std::string to, std::string numer); + material_handle DestinationFind( std::string Destination ); void OverheadTrack(float o); double MED[9][8]; // lista zmiennych do debugowania hamulca ED diff --git a/Globals.h b/Globals.h index bfe3a2a6..2b1f9dd8 100644 --- a/Globals.h +++ b/Globals.h @@ -70,7 +70,6 @@ struct global_settings { bool bLiveTraction{ true }; float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f }; - double fFogStart{ 1700 }; double fFogEnd{ 2000 }; std::string Season{}; // season of the year, based on simulation date std::string Weather{ "cloudy:" }; // current weather diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index ee7031be..6902be65 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1354,6 +1354,7 @@ public: std::string StLinSwitchType; bool Heating = false; //ogrzewanie 'Winger 020304 + bool HeatingAllow { false }; // heating switch // TODO: wrap heating in a basic device int DoubleTr = 1; //trakcja ukrotniona - przedni pojazd 'Winger 160304 bool PhysicActivation = true; @@ -1500,6 +1501,7 @@ public: /*-funkcje typowe dla lokomotywy elektrycznej*/ void ConverterCheck( double const Timestep ); // przetwornica + void HeatingCheck( double const Timestep ); void WaterPumpCheck( double const Timestep ); void WaterHeaterCheck( double const Timestep ); void FuelPumpCheck( double const Timestep ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b62d6a95..25875f18 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1075,11 +1075,13 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap for( int side = 0; side < 2; ++side ) { // przekazywanie napiec - auto const oppositeside = ( side == end::front ? end::rear : end::front ); + auto const oppositeside { ( side == end::front ? end::rear : end::front ) }; + auto const liveconnection{ + ( Couplers[ side ].CouplingFlag & ctrain_power ) + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) }; - if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + if( liveconnection ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ Couplers[ side ].ConnectedNr ]; Couplers[ oppositeside ].power_high.voltage = std::max( @@ -1103,8 +1105,8 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap Couplers[ side ].power_high.local = false; // power, if any, will be from external source if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == end::front ? @@ -1127,8 +1129,8 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap Couplers[ side ].power_high.local = true; // power is coming from local pantographs if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == end::front ? @@ -1409,6 +1411,8 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { // w rozrządczym nie (jest błąd w FIZ!) - Ra 2014-07: teraz we wszystkich UpdatePantVolume( Deltatime ); // Ra 2014-07: obsługa zbiornika rozrządu oraz pantografów } + // heating + HeatingCheck( Deltatime ); UpdateBrakePressure(Deltatime); UpdatePipePressure(Deltatime); @@ -1462,7 +1466,8 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { if( ( ConverterAllow ) && ( ConverterAllowLocal ) && ( false == PantPressLockActive ) - && ( Mains ) ) { + && ( ( Mains ) + || ( GetTrainsetVoltage() > 0 ) ) ) { // delay timer can be optionally configured, and is set anew whenever converter goes off if( ConverterStartDelayTimer <= 0.0 ) { ConverterFlag = true; @@ -1477,6 +1482,17 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { } }; +// heating system status check +void TMoverParameters::HeatingCheck( double const Timestep ) { + + Heating = ( + ( true == HeatingAllow ) + // powered vehicles are generally required to activate their power source to provide heating + // passive vehicles get a pass in this regard + && ( ( Power < 0.1 ) + || ( true == Mains ) ) ); +} + // water pump status check void TMoverParameters::WaterPumpCheck( double const Timestep ) { // NOTE: breaker override with start type is sm42 specific hack, replace with ability to define the presence of the breaker @@ -7055,9 +7071,14 @@ TMoverParameters::update_doors( double const Deltatime ) { door.local_close = door.local_close && ( false == door.is_closed ); door.remote_close = door.remote_close && ( false == door.is_closed ); + auto const autoopenrequest { + ( Doors.open_control == control_t::autonomous ) + && ( ( false == Doors.permit_needed ) || door.open_permit ) + }; auto const openrequest { ( localopencontrol && door.local_open ) - || ( remoteopencontrol && door.remote_open ) }; + || ( remoteopencontrol && door.remote_open ) + || ( autoopenrequest && ( false == door.is_open ) ) }; auto const autocloserequest { ( ( Doors.auto_velocity != -1.f ) && ( Vel > Doors.auto_velocity ) ) @@ -7279,14 +7300,14 @@ double TMoverParameters::GetTrainsetVoltage(void) return std::max( ( ( ( Couplers[end::front].Connected ) && ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) ) ) ) ? + || ( ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) + && ( Couplers[ end::front ].Connected->Heating ) ) ) ) ? Couplers[end::front].Connected->Couplers[ Couplers[end::front].ConnectedNr ].power_high.voltage : 0.0 ), ( ( ( Couplers[end::rear].Connected ) && ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) ) ) ) ? + || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) + && ( Couplers[ end::rear ].Connected->Heating ) ) ) ) ? Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.voltage : 0.0 ) ); } @@ -8344,12 +8365,6 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { lookup != doorcontrols.end() ? lookup->second : control_t::passenger; - - if( Doors.close_control == control_t::autonomous ) { - // convert legacy method - Doors.close_control = control_t::passenger; - Doors.auto_velocity = 10.0; - } } // automatic closing conditions extract_value( Doors.auto_duration, "DoorStayOpen", line, "" ); diff --git a/Model3d.cpp b/Model3d.cpp index 2de55e17..52a24399 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -789,6 +789,27 @@ int TSubModel::count_children() { 1 + Child->count_siblings() ); } +// locates submodel mapped with replacable -4 +std::tuple +TSubModel::find_replacable4() { + + if( m_material == -4 ) { + return std::make_tuple( this, ( fLight != -1.0 ) ); + } + + if( Next != nullptr ) { + auto lookup { Next->find_replacable4() }; + if( std::get( lookup ) != nullptr ) { return lookup; } + } + + if( Child != nullptr ) { + auto lookup { Child->find_replacable4() }; + if( std::get( lookup ) != nullptr ) { return lookup; } + } + + return std::make_tuple( nullptr, false ); +} + int TSubModel::FlagsCheck() { // analiza koniecznych zmian pomiędzy submodelami // samo pomijanie glBindTexture() nie poprawi wydajności diff --git a/Model3d.h b/Model3d.h index 6a1536c3..412c1f2b 100644 --- a/Model3d.h +++ b/Model3d.h @@ -166,6 +166,8 @@ public: TSubModel * ChildGet() { return Child; }; int count_siblings(); int count_children(); + // locates submodel mapped with replacable -4 + std::tuple find_replacable4(); int TriangleAdd(TModel3d *m, material_handle tex, int tri); void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotateXYZ( Math3D::vector3 vNewAngles); diff --git a/PyInt.cpp b/PyInt.cpp index 6ee0891f..e36fd903 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -10,9 +10,8 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "PyInt.h" -#include "Globals.h" +#include "dictionary.h" #include "application.h" -#include "renderer.h" #include "Logs.h" void render_task::run() { diff --git a/PyInt.h b/PyInt.h index db097d9b..36880d42 100644 --- a/PyInt.h +++ b/PyInt.h @@ -23,24 +23,6 @@ http://mozilla.org/MPL/2.0/. #define PyGetBool(param) param ? Py_True : Py_False #define PyGetString(param) PyString_FromString(param) -// collection of keyword-value pairs -// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity -struct dictionary_source { -// types - template - using keyvaluepair_sequence = std::vector>; -// members - keyvaluepair_sequence floats; - keyvaluepair_sequence integers; - keyvaluepair_sequence bools; - keyvaluepair_sequence strings; -// methods - inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); } -}; - // TODO: extract common base and inherit specialization from it class render_task { diff --git a/Texture.cpp b/Texture.cpp index e21893bc..11872d34 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -20,11 +20,13 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "application.h" -#include "utilities.h" +#include "dictionary.h" #include "Globals.h" #include "Logs.h" +#include "utilities.h" #include "sn_utils.h" + #define EU07_DEFERRED_TEXTURE_UPLOAD texture_manager::texture_manager() { @@ -96,17 +98,9 @@ void opengl_texture::make_request() { auto const components { Split( name, '?' ) }; - auto const query { Split( components.back(), '&' ) }; - auto *dictionary { new dictionary_source }; - if( dictionary != nullptr ) { - for( auto const &querypair : query ) { - auto const valuepos { querypair.find( '=' ) }; - dictionary->insert( - ToLower( querypair.substr( 0, valuepos ) ), - querypair.substr( valuepos + 1 ) ); - } - } + auto *dictionary { new dictionary_source( components.back() ) }; + if( dictionary == nullptr ) { return; } Application.request( { ToLower( components.front() ), dictionary, id } ); } diff --git a/Train.cpp b/Train.cpp index 2a437c25..a38c799c 100644 --- a/Train.cpp +++ b/Train.cpp @@ -30,6 +30,7 @@ http://mozilla.org/MPL/2.0/. #include "Console.h" #include "application.h" #include "renderer.h" +#include "dictionary.h" /* namespace input { @@ -543,31 +544,8 @@ dictionary_source *TTrain::GetTrainState() { dict->insert( "velnext", driver->VelNext ); dict->insert( "actualproximitydist", driver->ActualProximityDist ); // train data - auto const *timetable{ driver->TrainTimetable() }; - - dict->insert( "trainnumber", driver->TrainName() ); - dict->insert( "train_brakingmassratio", timetable->BrakeRatio ); - dict->insert( "train_enginetype", timetable->LocSeries ); - dict->insert( "train_engineload", timetable->LocLoad ); - - dict->insert( "train_stationindex", driver->iStationStart ); - auto const stationcount { driver->StationCount() }; - dict->insert( "train_stationcount", stationcount ); - if( stationcount > 0 ) { - // timetable stations data, if there's any - for( auto stationidx = 1; stationidx <= stationcount; ++stationidx ) { - auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; - auto const &timetableline { timetable->TimeTable[ stationidx ] }; - dict->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) ); - dict->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) ); - dict->insert( ( stationlabel + "lctn" ), timetableline.km ); - dict->insert( ( stationlabel + "vmax" ), timetableline.vmax ); - dict->insert( ( stationlabel + "ah" ), timetableline.Ah ); - dict->insert( ( stationlabel + "am" ), timetableline.Am ); - dict->insert( ( stationlabel + "dh" ), timetableline.Dh ); - dict->insert( ( stationlabel + "dm" ), timetableline.Dm ); - } - } + driver->TrainTimetable()->serialize( dict ); + dict->insert( "train_stationstart", driver->iStationStart ); dict->insert( "train_atpassengerstop", driver->IsAtPassengerStop ); // world state data dict->insert( "scenario", Global.SceneryFile ); @@ -4153,7 +4131,7 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->mvControlled->Heating ) { + if( false == Train->mvControlled->HeatingAllow ) { // turn on OnCommand_heatingenable( Train, Command ); } @@ -4167,24 +4145,20 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command void TTrain::OnCommand_heatingenable( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + Train->mvControlled->HeatingAllow = true; // visual feedback Train->ggTrainHeatingButton.UpdateValue( 1.0, Train->dsbSwitch ); - - if( true == Train->mvControlled->Heating ) { return; } // already enabled - - Train->mvControlled->Heating = true; } } void TTrain::OnCommand_heatingdisable( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + Train->mvControlled->HeatingAllow = false; // visual feedback Train->ggTrainHeatingButton.UpdateValue( 0.0, Train->dsbSwitch ); - - if( false == Train->mvControlled->Heating ) { return; } // already disabled - - Train->mvControlled->Heating = false; } } @@ -4315,37 +4289,73 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman void TTrain::OnCommand_doorpermitleft( TTrain *Train, command_data const &Command ) { + if( Command.action == GLFW_REPEAT ) { return; } + if( Command.action == GLFW_PRESS ) { - Train->mvOccupied->PermitDoors( - ( Train->mvOccupied->ActiveCab == 1 ? + auto const side { ( + Train->mvOccupied->ActiveCab == 1 ? side::left : - side::right ) ); + side::right ) }; - // visual feedback - Train->ggDoorLeftPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + if( Train->ggDoorLeftPermitButton.type() == TGaugeType::push ) { + // impulse switch + Train->mvOccupied->PermitDoors( side ); + // visual feedback + Train->ggDoorLeftPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // two-state switch + auto const newstate { !( Train->mvOccupied->Doors.instances[ side ].open_permit ) }; + + Train->mvOccupied->PermitDoors( side, newstate ); + // visual feedback + Train->ggDoorLeftPermitButton.UpdateValue( ( newstate ? 1.0 : 0.0 ), Train->dsbSwitch ); + } } else if( Command.action == GLFW_RELEASE ) { - // visual feedback - Train->ggDoorLeftPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( Train->ggDoorLeftPermitButton.type() == TGaugeType::push ) { + // impulse switch + // visual feedback + Train->ggDoorLeftPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } void TTrain::OnCommand_doorpermitright( TTrain *Train, command_data const &Command ) { + if( Command.action == GLFW_REPEAT ) { return; } + if( Command.action == GLFW_PRESS ) { - Train->mvOccupied->PermitDoors( - ( Train->mvOccupied->ActiveCab == 1 ? + auto const side { ( + Train->mvOccupied->ActiveCab == 1 ? side::right : - side::left ) ); + side::left ) }; - // visual feedback - Train->ggDoorRightPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + if( Train->ggDoorRightPermitButton.type() == TGaugeType::push ) { + // impulse switch + Train->mvOccupied->PermitDoors( side ); + // visual feedback + Train->ggDoorRightPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // two-state switch + auto const newstate { !( Train->mvOccupied->Doors.instances[ side ].open_permit ) }; + + Train->mvOccupied->PermitDoors( side, newstate ); + // visual feedback + Train->ggDoorRightPermitButton.UpdateValue( ( newstate ? 1.0 : 0.0 ), Train->dsbSwitch ); + } } else if( Command.action == GLFW_RELEASE ) { - // visual feedback - Train->ggDoorRightPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( Train->ggDoorRightPermitButton.type() == TGaugeType::push ) { + // impulse switch + // visual feedback + Train->ggDoorRightPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } @@ -6027,7 +6037,6 @@ bool TTrain::Update( double const Deltatime ) //--------- // hunter-080812: poprawka na ogrzewanie w elektrykach - usuniete uzaleznienie od przetwornicy if( ( mvControlled->Heating == true ) - && ( mvControlled->Mains == true ) && ( mvControlled->ConvOvldFlag == false ) ) btLampkaOgrzewanieSkladu.Turn( true ); else @@ -7612,7 +7621,12 @@ void TTrain::set_cab_controls( int const Cab ) { 1.f : 0.f ) ); // doors - // NOTE: for the time being permit switches are presumed to be impulse switches + if( ggDoorLeftPermitButton.type() != TGaugeType::push ) { + ggDoorLeftPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].open_permit ? 1.f : 0.f ); + } + if( ggDoorRightPermitButton.type() != TGaugeType::push ) { + ggDoorRightPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].open_permit ? 1.f : 0.f ); + } ggDoorPermitPresetButton.PutValue( mvOccupied->Doors.permit_preset ); ggDoorLeftButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].is_closed ? 0.f : 1.f ); ggDoorRightButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].is_closed ? 0.f : 1.f ); @@ -7841,8 +7855,8 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co } // TODO: move viable dedicated lights to the automatic light array std::unordered_map const autolights = { - { "i-doorpermit_left:", &mvOccupied->Doors.instances[side::left].open_permit }, - { "i-doorpermit_right:", &mvOccupied->Doors.instances[ side::right ].open_permit }, + { "i-doorpermit_left:", &mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].open_permit }, + { "i-doorpermit_right:", &mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].open_permit }, { "i-doorstep:", &mvOccupied->Doors.step_enabled } }; { diff --git a/audio.cpp b/audio.cpp index 22a66729..558f325e 100644 --- a/audio.cpp +++ b/audio.cpp @@ -78,15 +78,15 @@ openal_buffer::openal_buffer( std::string const &Filename ) : // TBD, TODO: customized vorbis_decode to avoid unnecessary shuffling around of the decoded data int channels, samplerate; std::int16_t *filedata { nullptr }; - auto const samplecount{ stb_vorbis_decode_filename( Filename.c_str(), &channels, &samplerate, &filedata ) }; + auto const samplecount { stb_vorbis_decode_filename( Filename.c_str(), &channels, &samplerate, &filedata ) }; if( samplecount > 0 ) { rate = samplerate; - data.resize( samplecount ); - std::copy( filedata, filedata + samplecount, std::begin( data ) ); + data.resize( samplecount * channels ); + std::copy( filedata, filedata + data.size(), std::begin( data ) ); free( filedata ); if( channels > 1 ) { narrow_to_mono( channels ); - data.resize( samplecount / channels ); + data.resize( samplecount ); } } else { @@ -109,7 +109,7 @@ void openal_buffer::narrow_to_mono( std::uint16_t const Channelcount ) { std::size_t monodataindex { 0 }; - std::int32_t accumulator { 0 }; + std::int64_t accumulator { 0 }; auto channelcount { Channelcount }; for( auto const channeldata : data ) { @@ -117,7 +117,7 @@ openal_buffer::narrow_to_mono( std::uint16_t const Channelcount ) { accumulator += channeldata; if( --channelcount == 0 ) { - data[ monodataindex++ ] = accumulator / Channelcount; + data[ monodataindex++ ] = static_cast( accumulator / Channelcount ); accumulator = 0; channelcount = Channelcount; } diff --git a/dictionary.cpp b/dictionary.cpp new file mode 100644 index 00000000..f4e987ec --- /dev/null +++ b/dictionary.cpp @@ -0,0 +1,55 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "dictionary.h" + +#include "simulation.h" +#include "utilities.h" +#include "DynObj.h" +#include "Driver.h" +#include "mtable.h" + +dictionary_source::dictionary_source( std::string const &Input ) { + + auto const keyvaluepairs { Split( Input, '&' ) }; + + for( auto const &keyvaluepair : keyvaluepairs ) { + + auto const valuepos { keyvaluepair.find( '=' ) }; + if( keyvaluepair[ 0 ] != '$' ) { + // regular key, value pairs + insert( + ToLower( keyvaluepair.substr( 0, valuepos ) ), + keyvaluepair.substr( valuepos + 1 ) ); + } + else { + // special case, $key indicates request to include certain dataset clarified by value + auto const key { ToLower( keyvaluepair.substr( 0, valuepos ) ) }; + auto const value { keyvaluepair.substr( valuepos + 1 ) }; + + if( key == "$timetable" ) { + // timetable pulled from (preferably) the owner/direct controller of specified vehicle + auto const *vehicle { simulation::Vehicles.find( value ) }; + auto const *controller { ( + vehicle == nullptr ? nullptr : + vehicle->ctOwner == nullptr ? vehicle->Mechanik : + vehicle->ctOwner ) }; + auto const *timetable { ( + controller != nullptr ? + controller->TrainTimetable() : + nullptr ) }; + + if( timetable != nullptr ) { + timetable->serialize( this ); + } + } + } + } +} diff --git a/dictionary.h b/dictionary.h new file mode 100644 index 00000000..98828856 --- /dev/null +++ b/dictionary.h @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +// collection of keyword-value pairs +// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity +struct dictionary_source { +// types + template + using keyvaluepair_sequence = std::vector>; +// members + keyvaluepair_sequence floats; + keyvaluepair_sequence integers; + keyvaluepair_sequence bools; + keyvaluepair_sequence strings; +// constructors + dictionary_source() = default; + dictionary_source( std::string const &Input ); +// methods + inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); } +}; diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 5792999b..e74cc17c 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -21,7 +21,7 @@ http://mozilla.org/MPL/2.0/. #include "uilayer.h" #include "Logs.h" -auto const EU07_CONTROLLER_MOUSESLIDERSIZE{ 0.65 }; +auto const EU07_CONTROLLER_MOUSESLIDERSIZE{ 0.6 }; void mouse_slider::bind( user_command const &Command ) { diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index d93a3b07..26cef8b1 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -339,6 +339,9 @@ Source Files + + Source Files + @@ -623,6 +626,9 @@ Header Files + + Header Files + diff --git a/mtable.cpp b/mtable.cpp index bf910011..6aed55d0 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include "Globals.h" #include "simulationtime.h" +#include "dictionary.h" #include "utilities.h" double TTrainParameters::CheckTrainLatency() @@ -523,3 +524,31 @@ bool TTrainParameters::DirectionChange() return true; return false; } + +void TTrainParameters::serialize( dictionary_source *Output ) const { + + Output->insert( "trainnumber", TrainName ); + Output->insert( "train_brakingmassratio", BrakeRatio ); + Output->insert( "train_enginetype", LocSeries ); + Output->insert( "train_engineload", LocLoad ); + + Output->insert( "train_stationfrom", Relation1 ); + Output->insert( "train_stationto", Relation2 ); + Output->insert( "train_stationindex", StationIndex ); + Output->insert( "train_stationcount", StationCount ); + if( StationCount > 0 ) { + // timetable stations data, if there's any + for( auto stationidx = 1; stationidx <= StationCount; ++stationidx ) { + auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; + auto const &timetableline { TimeTable[ stationidx ] }; + Output->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) ); + Output->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) ); + Output->insert( ( stationlabel + "lctn" ), timetableline.km ); + Output->insert( ( stationlabel + "vmax" ), timetableline.vmax ); + Output->insert( ( stationlabel + "ah" ), timetableline.Ah ); + Output->insert( ( stationlabel + "am" ), timetableline.Am ); + Output->insert( ( stationlabel + "dh" ), timetableline.Dh ); + Output->insert( ( stationlabel + "dm" ), timetableline.Dm ); + } + } +} diff --git a/mtable.h b/mtable.h index ea905e4c..78816aaa 100644 --- a/mtable.h +++ b/mtable.h @@ -83,6 +83,7 @@ class TTrainParameters bool LoadTTfile(std::string scnpath, int iPlus, double vmax); bool DirectionChange(); void StationIndexInc(); + void serialize( dictionary_source *Output ) const; }; class TMTableTime diff --git a/ref/stb/stb_vorbis.c b/ref/stb/stb_vorbis.c index 69a23bca..ff117abe 100644 --- a/ref/stb/stb_vorbis.c +++ b/ref/stb/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.14 - public domain +// Ogg Vorbis audio decoder - v1.16 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -33,6 +33,8 @@ // Timur Gagiev // // Partial history: +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files @@ -253,7 +255,7 @@ extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. // note that stb_vorbis must "own" this stream; if you seek it in between -// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to // perform stb_vorbis_seek_*() operations on this file, it will assume it // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. @@ -374,7 +376,8 @@ enum STBVorbisError VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, - VORBIS_seek_failed + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported }; @@ -1073,7 +1076,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); - // propogate availability up the tree + // propagate availability up the tree if (z != len[i]) { assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { @@ -2637,7 +2640,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) // once I combined the passes. // so there's a missing 'times 2' here (for adding X to itself). - // this propogates through linearly to the end, where the numbers + // this propagates through linearly to the end, where the numbers // are 1/2 too small, and need to be compensated for. { @@ -3578,7 +3581,22 @@ static int start_decoder(vorb *f) if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); // check for expected packet length if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); - if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + // read packet // check packet header if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); @@ -4566,7 +4584,7 @@ static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) return 1; } -// rarely used function to seek back to the preceeding page while finding the +// rarely used function to seek back to the preceding page while finding the // start of a packet static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { @@ -4973,7 +4991,13 @@ stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, con stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) { - FILE *f = fopen(filename, "rb"); + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; @@ -5459,4 +5483,4 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ -*/ \ No newline at end of file +*/ diff --git a/renderer.cpp b/renderer.cpp index e9e16df2..1840192d 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -32,17 +32,18 @@ int const EU07_ENVIRONMENTBUFFERSIZE { 256 }; // size of (square) environmental void opengl_light::apply_intensity( float const Factor ) { - if( Factor == 1.0 ) { + if( Factor == 1.f ) { ::glLightfv( id, GL_AMBIENT, glm::value_ptr( ambient ) ); ::glLightfv( id, GL_DIFFUSE, glm::value_ptr( diffuse ) ); ::glLightfv( id, GL_SPECULAR, glm::value_ptr( specular ) ); } else { + auto const factor{ clamp( Factor, 0.05f, 1.f ) }; // temporary light scaling mechanics (ultimately this work will be left to the shaders - glm::vec4 scaledambient( ambient.r * Factor, ambient.g * Factor, ambient.b * Factor, ambient.a ); - glm::vec4 scaleddiffuse( diffuse.r * Factor, diffuse.g * Factor, diffuse.b * Factor, diffuse.a ); - glm::vec4 scaledspecular( specular.r * Factor, specular.g * Factor, specular.b * Factor, specular.a ); + glm::vec4 scaledambient( ambient.r * factor, ambient.g * factor, ambient.b * factor, ambient.a ); + glm::vec4 scaleddiffuse( diffuse.r * factor, diffuse.g * factor, diffuse.b * factor, diffuse.a ); + glm::vec4 scaledspecular( specular.r * factor, specular.g * factor, specular.b * factor, specular.a ); glLightfv( id, GL_AMBIENT, glm::value_ptr( scaledambient ) ); glLightfv( id, GL_DIFFUSE, glm::value_ptr( scaleddiffuse ) ); glLightfv( id, GL_SPECULAR, glm::value_ptr( scaledspecular ) ); @@ -2170,34 +2171,36 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { m_renderspecular = true; // vehicles are rendered with specular component. static models without, at least for the time being // render if( Dynamic->mdLowPolyInt ) { - // low poly interior - /* - if( ( true == FreeFlyModeFlag ) - || ( ( Dynamic->mdKabina == nullptr ) || ( false == Dynamic->bDisplayCab ) ) ) { - */ -/* - // enable cab light if needed - if( Dynamic->InteriorLightLevel > 0.0f ) { + // HACK: reduce light level for vehicle interior if there's strong global lighting source + auto const luminance { static_cast( 0.5 * ( std::max( 0.3, Global.fLuminance - Global.Overcast ) ) ) }; + m_sunlight.apply_intensity( + clamp( ( + Dynamic->fShade > 0.f ? + Dynamic->fShade : + 1.f ) + - luminance, + 0.f, 1.f ) ); - // crude way to light the cabin, until we have something more complete in place - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Dynamic->InteriorLightLevel ) ); - } -*/ - Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); -/* - if( Dynamic->InteriorLightLevel > 0.0f ) { - // reset the overall ambient - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( m_baseambient ) ); - } -*/ - /* + // low poly interior + Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); + // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); } - */ + + m_sunlight.apply_intensity( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f ); } - if( Dynamic->mdModel ) + else { + // HACK: if the model lacks low poly interior, we presume the load is placed outside + if( Dynamic->mdLoad ) { + // renderowanie nieprzezroczystego ładunku + Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } + } + if( Dynamic->mdModel ) { Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); - if( Dynamic->mdLoad ) // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); + } // post-render cleanup m_renderspecular = false; if( Dynamic->fShade > 0.0f ) { diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index 2cbfdbcc..dbf53ff1 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -164,14 +164,22 @@ state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratch // atmosphere color; legacy parameter, no longer used Input.getTokens( 3 ); // fog range - Input.getTokens( 2 ); - Input - >> Global.fFogStart - >> Global.fFogEnd; + { + double fograngestart, fograngeend; + Input.getTokens( 2 ); + Input + >> fograngestart + >> fograngeend; - if( Global.fFogEnd > 0.0 ) { - // fog colour; optional legacy parameter, no longer used - Input.getTokens( 3 ); + if( Global.fFogEnd != 0.0 ) { + // fog colour; optional legacy parameter, no longer used + Input.getTokens( 3 ); + } + + Global.fFogEnd = + clamp( + Random( fograngestart, fograngeend ), + 100.0, 2000.0 ); } std::string token { Input.getToken() }; diff --git a/version.h b/version.h index 7ca02639..0f25130e 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 19 -#define VERSION_MINOR 327 +#define VERSION_MINOR 406 #define VERSION_REVISION 0