16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-23 06:19:18 +02:00

build 200124. scenario parameters control sliders, material reload support, material opacity driven translucent geometry alpha test, command keyboard mapping tooltip hints, camshaft logic tweak, motor connector logic tweak, ai power use logic tweak, ai pantograph control logic tweak, converter sound pitch fix. additional cab control localization

This commit is contained in:
tmj-fstate
2020-01-24 17:25:57 +01:00
parent a501c09b0f
commit f14b2c1343
32 changed files with 709 additions and 279 deletions

View File

@@ -2194,6 +2194,7 @@ bool TController::CheckVehicles(TOrders user)
}
// with the order established the virtual train manager can do their work
p = pVehicles[0];
ControlledEnginesCount = ( p->MoverParameters->Power > 1.0 ? 1 : 0 );
while (p)
{
if( p != pVehicle ) {
@@ -2207,6 +2208,11 @@ bool TController::CheckVehicles(TOrders user)
p->MoverParameters->ConverterSwitch( true, range_t::local );
}
}
else {
if( p->MoverParameters->Power > 1.0 ) {
++ControlledEnginesCount;
}
}
}
if (p->asDestination == "none")
@@ -2537,9 +2543,6 @@ bool TController::PrepareEngine()
}
if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector)
{ // jeśli silnikowy jest pantografującym
mvControlling->OperatePantographsValve( operation_t::enable );
mvControlling->OperatePantographValve( end::front, operation_t::enable );
mvControlling->OperatePantographValve( end::rear, operation_t::enable );
if (mvControlling->PantPress < 4.2) {
// załączenie małej sprężarki
if( false == mvControlling->PantAutoValve ) {
@@ -2550,11 +2553,15 @@ bool TController::PrepareEngine()
}
else {
// jeżeli jest wystarczające ciśnienie w pantografach
if ((!mvControlling->bPantKurek3) ||
(mvControlling->PantPress <=
mvControlling->ScndPipePress)) // kurek przełączony albo główna już pompuje
if ((!mvControlling->bPantKurek3)
|| (mvControlling->PantPress <= mvControlling->ScndPipePress)) // kurek przełączony albo główna już pompuje
mvControlling->PantCompFlag = false; // sprężarkę pantografów można już wyłączyć
}
if( ( fOverhead2 == -1.0 ) && ( iOverheadDown == 0 ) ) {
mvControlling->OperatePantographsValve( operation_t::enable );
mvControlling->OperatePantographValve( end::front, operation_t::enable );
mvControlling->OperatePantographValve( end::rear, operation_t::enable );
}
}
}
@@ -3163,7 +3170,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 ) };
auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) * ControlledEnginesCount };
OK = (
sufficientpowermargin ?
@@ -3183,7 +3190,7 @@ bool TController::IncSpeed()
mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ?
mvControlling->EnginePowerSource.CollectorParameters.MinV :
useseriesmodevoltage )
> ( IsHeavyCargoTrain ? 80.0 : 60.0 ) };
> ( IsHeavyCargoTrain ? 80.0 : 60.0 ) * ControlledEnginesCount };
OK = (
( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ?
@@ -3994,8 +4001,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
if (NewCommand == "Overhead")
{ // informacja o stanie sieci trakcyjnej
fOverhead1 =
NewValue1; // informacja o napięciu w sieci trakcyjnej (0=brak drutu, zatrzymaj!)
fOverhead1 = NewValue1; // informacja o napięciu w sieci trakcyjnej (0=brak drutu, zatrzymaj!)
fOverhead2 = NewValue2; // informacja o sposobie jazdy (-1=normalnie, 0=bez prądu, >0=z
// opuszczonym i ograniczeniem prędkości)
return true; // załatwione
@@ -4700,8 +4706,12 @@ TController::UpdateSituation(double dt) {
if( ( fOverhead2 > 0.0 ) || iOverheadDown ) {
// jazda z opuszczonymi pantografami
mvControlling->OperatePantographValve( end::front, operation_t::disable );
mvControlling->OperatePantographValve( end::rear, operation_t::disable );
if( mvControlling->Pantographs[ end::front ].is_active ) {
mvControlling->OperatePantographValve( end::front, operation_t::disable );
}
if( mvControlling->Pantographs[ end::rear ].is_active ) {
mvControlling->OperatePantographValve( end::rear, operation_t::disable );
}
}
else {
// jeśli nie trzeba opuszczać pantografów
@@ -4779,15 +4789,17 @@ TController::UpdateSituation(double dt) {
// NOTE: abs(stoptime) covers either at least 15 sec remaining for a scheduled stop, or 15+ secs spent at a basic stop
&& ( std::abs( fStopTime ) > 15.0 ) ) {
// spending a longer at a stop, raise also front pantograph
if( ( iDirection >= 0 ) && ( useregularpantographlayout ) ) {
// jak jedzie w kierunku sprzęgu 0
if( mvControlling->PantFrontVolt == 0.0 ) {
mvControlling->OperatePantographValve( end::front, operation_t::enable );
if( mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) {
if( ( iDirection >= 0 ) && ( useregularpantographlayout ) ) {
// jak jedzie w kierunku sprzęgu 0
if( mvControlling->PantFrontVolt == 0.0 ) {
mvControlling->OperatePantographValve( end::front, operation_t::enable );
}
}
}
else {
if( mvControlling->PantRearVolt == 0.0 ) {
mvControlling->OperatePantographValve( end::rear, operation_t::enable );
else {
if( mvControlling->PantRearVolt == 0.0 ) {
mvControlling->OperatePantographValve( end::rear, operation_t::enable );
}
}
}
}

View File

@@ -459,6 +459,7 @@ private:
double fMass = 0.0; // całkowita masa do liczenia stycznej składowej grawitacji
double fAccGravity = 0.0; // przyspieszenie składowej stycznej grawitacji
int iVehicles = 0; // ilość pojazdów w składzie
int ControlledEnginesCount{ 0 }; // number of powered vehicles under driver's control
bool iEngineActive{ false }; // ABu: Czy silnik byl juz zalaczony
bool IsCargoTrain{ false };
bool IsHeavyCargoTrain{ false };

View File

@@ -3805,24 +3805,28 @@ void TDynamicObject::RenderSounds() {
m_startjoltplayed = false;
}
double const dt{ Timer::GetDeltaRenderTime() };
double volume{ 0.0 };
double frequency{ 1.0 };
auto const dt{ Timer::GetDeltaRenderTime() };
m_powertrainsounds.render( *MoverParameters, dt );
auto volume{ 0.0 };
auto frequency{ 1.0 };
// NBMX dzwiek przetwornicy
if( MoverParameters->ConverterFlag ) {
frequency = (
MoverParameters->EngineType != TEngineType::ElectricSeriesMotor ?
1.0 :
MoverParameters->PantographVoltage > 0.0 ?
MoverParameters->PantographVoltage / ( MoverParameters->NominalVoltage * MoverParameters->RList[ MoverParameters->RlistSize ].Mn ) :
1.0 );
frequency = sConverter.m_frequencyoffset + sConverter.m_frequencyfactor * frequency;
sConverter
.pitch( clamp( frequency, 0.75, 1.25 ) ) // arbitrary limits )
.play( sound_flags::exclusive | sound_flags::looping );
if( MoverParameters->EngineType == TEngineType::ElectricSeriesMotor ) {
auto const voltage { std::max( MoverParameters->GetAnyTrainsetVoltage(), MoverParameters->PantographVoltage ) };
if( voltage > 0.0 ) {
// NOTE: we do sound modulation here to avoid sudden jump on voltage loss
frequency = ( voltage / ( MoverParameters->NominalVoltage * MoverParameters->RList[ MoverParameters->RlistSize ].Mn ) );
frequency *= sConverter.m_frequencyfactor + sConverter.m_frequencyoffset;
sConverter.pitch( clamp( frequency, 0.75, 1.25 ) ); // arbitrary limits )
}
}
else {
frequency = sConverter.m_frequencyoffset + sConverter.m_frequencyfactor * frequency;
sConverter.pitch( clamp( frequency, 0.75, 1.25 ) ); // arbitrary limits )
}
sConverter.play( sound_flags::exclusive | sound_flags::looping );
}
else {
sConverter.stop();
@@ -6522,14 +6526,12 @@ void TDynamicObject::OverheadTrack(float o)
}
else if (o > 0.0)
{ // opuszczenie pantografów
ctOwner->iOverheadZero |=
iOverheadMask; // ustawienie bitu - ma jechać bez pobierania prądu
ctOwner->iOverheadZero |= iOverheadMask; // ustawienie bitu - ma jechać bez pobierania prądu
ctOwner->iOverheadDown |= iOverheadMask; // ustawienie bitu - ma opuścić pantograf
}
else
{ // jazda bezprądowa z podniesionym pantografem
ctOwner->iOverheadZero |=
iOverheadMask; // ustawienie bitu - ma jechać bez pobierania prądu
ctOwner->iOverheadZero |= iOverheadMask; // ustawienie bitu - ma jechać bez pobierania prądu
ctOwner->iOverheadDown &= ~iOverheadMask; // zerowanie bitu - może podnieść pantograf
}
}

View File

@@ -83,7 +83,7 @@ struct global_settings {
std::string Weather{ "cloudy:" }; // current weather
bool FullPhysics{ true }; // full calculations performed for each simulation step
bool bnewAirCouplers{ true };
double fMoveLight{ -1 }; // numer dnia w roku albo -1
float fMoveLight{ -1.f }; // numer dnia w roku albo -1
bool FakeLight{ false }; // toggle between fixed and dynamic daylight
double fTimeSpeed{ 1.0 }; // przyspieszenie czasu, zmienna do testów
double fLatitudeDeg{ 52.0 }; // szerokość geograficzna

View File

@@ -170,13 +170,13 @@ enum class range_t {
};
// possible settings of enable/disable input pair; exclusive
enum class operation_t {
enable_on = 1,
enable_off = -1,
disable_on = 2,
disable_off = -2,
none = 0,
enable,
disable,
enable_on,
enable_off,
disable_on,
disable_off,
};
// start method for devices; exclusive
enum class start_t {
@@ -1380,6 +1380,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
bool LineBreakerClosesAtNoPowerPosOnly{ false };
int MainCtrlPos = 0; /*polozenie glownego nastawnika*/
int ScndCtrlPos = 0; /*polozenie dodatkowego nastawnika*/
int LightsPos = 0; /*polozenie przelacznika wielopozycyjnego swiatel*/

View File

@@ -3193,6 +3193,7 @@ void TMoverParameters::MainSwitch_( bool const State ) {
&& ( true == NoVoltRelay )
&& ( true == OvervoltageRelay )
&& ( LastSwitchingTime > CtrlDelay )
&& ( HasCamshaft ? IsMainCtrlActualNoPowerPos() : ( LineBreakerClosesAtNoPowerPosOnly ? IsMainCtrlNoPowerPos() : true ) )
&& ( false == TestFlag( DamageFlag, dtrain_out ) )
&& ( false == TestFlag( EngDmgFlag, 1 ) ) ) ) {
@@ -5086,8 +5087,12 @@ double TMoverParameters::TractionForce( double dt ) {
case TEngineType::DieselElectric: {
// TODO: move this to the auto relay check when the electric engine code paths are unified
StLinFlag = MotorConnectorsCheck();
StLinFlag &= IsMainCtrlNoPowerPos();
StLinFlag &= MotorConnectorsCheck();
StLinFlag |= (
( MainCtrlActualPos == 0 )
&& ( TrainType != dt_EZT ?
MainCtrlPowerPos() == 1 :
MainCtrlPowerPos() > 0 ) );
break;
}
@@ -6145,12 +6150,14 @@ bool TMoverParameters::AutoRelayCheck(void)
bool OK = false; // b:int;
bool ARC = false;
auto const motorconnectors { MotorConnectorsCheck() };
auto const motorconnectorsoff { false == MotorConnectorsCheck() };
// Ra 2014-06: dla SN61 nie działa prawidłowo
// yBARC - rozlaczenie stycznikow liniowych
if( ( false == motorconnectors )
|| ( HasCamshaft ? IsMainCtrlActualNoPowerPos() : IsMainCtrlNoPowerPos() ) ) {
if( ( motorconnectorsoff )
|| ( HasCamshaft ?
IsMainCtrlActualNoPowerPos() :
IsMainCtrlNoPowerPos() ) ) {
StLinFlag = false;
OK = false;
if( false == DynamicBrakeFlag ) {
@@ -6329,7 +6336,12 @@ bool TMoverParameters::AutoRelayCheck(void)
{
OK = false;
// ybARC - zalaczenie stycznikow liniowych
if( true == motorconnectors ) {
if( ( false == motorconnectorsoff )
&& ( MainCtrlActualPos == 0 )
&& ( ( TrainType == dt_EZT || HasCamshaft ) ?
MainCtrlPowerPos() > 0 :
MainCtrlPowerPos() == 1 ) ) {
DelayCtrlFlag = true;
if( LastRelayTime >= InitialCtrlDelay ) {
StLinFlag = true;
@@ -6339,14 +6351,15 @@ bool TMoverParameters::AutoRelayCheck(void)
OK = true;
}
}
else
else {
DelayCtrlFlag = false;
}
if( ( false == StLinFlag )
&& ( ( MainCtrlActualPos > 0 )
|| ( ScndCtrlActualPos > 0 ) ) ) {
if( CoupledCtrl ) {
if( CoupledCtrl || HasCamshaft ) {
if( TrainType == dt_EZT ) {
// EN57 wal jednokierunkowy calosciowy
@@ -6380,7 +6393,8 @@ bool TMoverParameters::AutoRelayCheck(void)
else {
// wal kulakowy dwukierunkowy
if( LastRelayTime > CtrlDownDelay ) {
if( ScndCtrlActualPos > 0 ) {
if( ( CoupledCtrl )
&& ( ScndCtrlActualPos > 0 ) ) {
--ScndCtrlActualPos;
SetFlag( SoundFlag, sound::shuntfield );
}
@@ -6421,16 +6435,7 @@ bool TMoverParameters::MotorConnectorsCheck() {
|| ( true == StLinSwitchOff )
|| ( DirActive == 0 ) };
if( connectorsoff ) { return false; }
auto const connectorson {
( true == StLinFlag )
|| ( ( MainCtrlActualPos == 0 )
&& ( ( TrainType != dt_EZT ?
MainCtrlPowerPos() == 1 :
MainCtrlPowerPos() > 0 ) ) ) };
return connectorson;
return ( false == connectorsoff );
}
bool TMoverParameters::OperatePantographsValve( operation_t const State, range_t const Notify ) {
@@ -6439,8 +6444,15 @@ bool TMoverParameters::OperatePantographsValve( operation_t const State, range_t
auto &valve { PantsValve };
valve.is_enabled = ( State == operation_t::enable );
valve.is_disabled = ( State == operation_t::disable );
switch( State ) {
case operation_t::none: { valve.is_enabled = false; valve.is_disabled = false; break; }
case operation_t::enable: { valve.is_enabled = true; valve.is_disabled = false; break; }
case operation_t::disable: { valve.is_enabled = false; valve.is_disabled = true; break; }
case operation_t::enable_on: { valve.is_enabled = true; break; }
case operation_t::enable_off: { valve.is_enabled = false; break; }
case operation_t::disable_on: { valve.is_disabled = true; break; }
case operation_t::disable_off: { valve.is_disabled = false; break; }
}
if( Notify != range_t::local ) {
SendCtrlToNext(
@@ -6459,8 +6471,15 @@ bool TMoverParameters::OperatePantographValve( end const End, operation_t const
auto &valve { Pantographs[ End ].valve };
valve.is_enabled = ( State == operation_t::enable );
valve.is_disabled = ( State == operation_t::disable );
switch( State ) {
case operation_t::none: { valve.is_enabled = false; valve.is_disabled = false; break; }
case operation_t::enable: { valve.is_enabled = true; valve.is_disabled = false; break; }
case operation_t::disable: { valve.is_enabled = false; valve.is_disabled = true; break; }
case operation_t::enable_on: { valve.is_enabled = true; break; }
case operation_t::enable_off: { valve.is_enabled = false; break; }
case operation_t::disable_on: { valve.is_disabled = true; break; }
case operation_t::disable_off: { valve.is_disabled = false; break; }
}
if( Notify != range_t::local ) {
SendCtrlToNext(

View File

@@ -353,6 +353,9 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
if( Opacity > 1.f ) {
Opacity = std::min( 1.f, Opacity * 0.01f );
}
if( Opacity < -1.f ) {
Opacity = std::max( -1.f, Opacity * 0.01f );
}
if (!parser.expectToken("map:"))
Error("Model map parse failure!");

View File

@@ -1284,7 +1284,7 @@ texture_manager::info() const {
}
return
"; textures: "
"textures: "
#ifdef EU07_DEFERRED_TEXTURE_UPLOAD
+ std::to_string( readytexturecount )
+ " ("

117
Train.cpp
View File

@@ -39,11 +39,19 @@ extern user_command command;
}
*/
void
control_mapper::clear() {
*this = control_mapper();
}
void
control_mapper::insert( TGauge const &Gauge, std::string const &Label ) {
if( Gauge.SubModel != nullptr ) { m_controlnames.emplace( Gauge.SubModel, Label ); }
if( Gauge.SubModelOn != nullptr ) { m_controlnames.emplace( Gauge.SubModelOn, Label ); }
m_names.emplace( Label );
}
std::string
@@ -58,6 +66,12 @@ control_mapper::find( TSubModel const *Control ) const {
}
}
bool
control_mapper::contains( std::string const Control ) const {
return ( m_names.find( Control ) != m_names.end() );
}
void TCab::Load(cParser &Parser)
{
// NOTE: clearing control tables here is bit of a crutch, imposed by current scheme of loading compartments anew on each cab change
@@ -2075,10 +2089,16 @@ void TTrain::OnCommand_pantographraisefront( TTrain *Train, command_data const &
// HACK: presence of pantograph selector prevents manual operation of the individual valves
if( Train->ggPantSelectButton.SubModel ) { return; }
// prevent operation without submodel outside of engine compartment
if( ( Train->iCabn != 0 )
&& ( false == Train->m_controlmapper.contains( "pantfront_sw:" ) ) ) { return; }
if( Command.action == GLFW_PRESS ) {
// only reacting to press, so the switch doesn't flip back and forth if key is held down
Train->mvControlled->OperatePantographValve( end::front, operation_t::enable );
Train->mvControlled->OperatePantographValve( end::front,
Train->mvOccupied->PantSwitchType == "impulse" ?
operation_t::enable_on :
operation_t::enable );
}
else if( Command.action == GLFW_RELEASE ) {
// NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch
@@ -2090,11 +2110,17 @@ void TTrain::OnCommand_pantographraiserear( TTrain *Train, command_data const &C
// HACK: presence of pantograph selector prevents manual operation of the individual valves
if( Train->ggPantSelectButton.SubModel ) { return; }
// prevent operation without submodel outside of engine compartment
if( ( Train->iCabn != 0 )
&& ( false == Train->m_controlmapper.contains( "pantrear_sw:" ) ) ) { return; }
if( Command.action == GLFW_PRESS ) {
// only reacting to press, so the switch doesn't flip back and forth if key is held down
Train->mvControlled->OperatePantographValve( end::rear, operation_t::enable );
}
Train->mvControlled->OperatePantographValve( end::rear,
Train->mvOccupied->PantSwitchType == "impulse" ?
operation_t::enable_on :
operation_t::enable );
}
else if( Command.action == GLFW_RELEASE ) {
// NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch
OnCommand_pantographtogglerear( Train, Command );
@@ -2105,10 +2131,21 @@ void TTrain::OnCommand_pantographlowerfront( TTrain *Train, command_data const &
// HACK: presence of pantograph selector prevents manual operation of the individual valves
if( Train->ggPantSelectButton.SubModel ) { return; }
// prevent operation without submodel outside of engine compartment
if( ( Train->iCabn != 0 )
&& ( false == Train->m_controlmapper.contains(
Train->mvOccupied->PantSwitchType == "impulse" ?
"pantfrontoff_sw:" :
"pantfront_sw:" ) ) ) {
return;
}
if( Command.action == GLFW_PRESS ) {
// only reacting to press, so the switch doesn't flip back and forth if key is held down
Train->mvControlled->OperatePantographValve( end::front, operation_t::disable );
Train->mvControlled->OperatePantographValve( end::front,
Train->mvOccupied->PantSwitchType == "impulse" ?
operation_t::disable_on :
operation_t::disable );
}
else if( Command.action == GLFW_RELEASE ) {
// NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch
@@ -2120,14 +2157,24 @@ void TTrain::OnCommand_pantographlowerrear( TTrain *Train, command_data const &C
// HACK: presence of pantograph selector prevents manual operation of the individual valves
if( Train->ggPantSelectButton.SubModel ) { return; }
if( ( Train->iCabn != 0 )
&& ( false == Train->m_controlmapper.contains(
Train->mvOccupied->PantSwitchType == "impulse" ?
"pantrearoff_sw:" :
"pantrear_sw:" ) ) ) {
return;
}
if( Command.action == GLFW_PRESS ) {
// only reacting to press, so the switch doesn't flip back and forth if key is held down
Train->mvControlled->OperatePantographValve( end::rear, operation_t::disable );
Train->mvControlled->OperatePantographValve( end::rear,
Train->mvOccupied->PantSwitchType == "impulse" ?
operation_t::disable_on :
operation_t::disable );
}
else if( Command.action == GLFW_RELEASE ) {
// NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch
OnCommand_pantographtogglefront( Train, Command );
OnCommand_pantographtogglerear( Train, Command );
}
}
@@ -6150,9 +6197,9 @@ bool TTrain::Update( double const Deltatime )
|| ( mvControlled->MainCtrlActualPos == 0 ) ); // do EU04
btLampkaStyczn.Turn(
mvControlled->StLinFlag ?
( ( mvOccupied->StLinFlag ) /* || ( mvOccupied->BrakePress > 2.0 ) || ( mvOccupied->PipePress < 3.6 ) */ ) ?
false :
mvOccupied->BrakePress < 1.0 ); // mozna prowadzic rozruch
( mvOccupied->BrakePress < 1.0 ) ); // mozna prowadzic rozruch
if( ( ( mvControlled->CabOccupied == 1 ) && ( TestFlag( mvControlled->Couplers[ end::rear ].CouplingFlag, coupling::control ) ) )
|| ( ( mvControlled->CabOccupied == -1 ) && ( TestFlag( mvControlled->Couplers[ end::front ].CouplingFlag, coupling::control ) ) ) ) {
@@ -6380,7 +6427,7 @@ bool TTrain::Update( double const Deltatime )
if( ( mover->StLinFlag )
|| ( mover->BrakePress > 2.0 )
|| ( mover->PipePress < 0.36 ) ) {
|| ( mover->PipePress < 3.6 ) ) {
btLampkaStycznB.Turn( false );
}
else if( mover->BrakePress < 1.0 ) {
@@ -6760,57 +6807,6 @@ bool TTrain::Update( double const Deltatime )
mvControlled->AntiSlippingBrake();
}
}
/*
// NOTE: crude way to have the pantographs go back up if they're dropped due to insufficient pressure etc
// TODO: rework it into something more elegant, when redoing the whole consist/unit/cab etc arrangement
if( ( DynamicObject->Mechanik == nullptr )
|| ( false == DynamicObject->Mechanik->AIControllFlag ) ) {
// don't mess with the ai driving, at least not while switches don't follow ai-set vehicle state
if( ( mvControlled->Battery )
|| ( mvControlled->ConverterFlag ) ) {
if( ggPantAllDownButton.GetDesiredValue() < 0.05 ) {
// the 'lower all' button overrides state of switches, while active itself
if( ( false == mvControlled->PantFrontUp )
&& ( ggPantFrontButton.GetDesiredValue() >= 0.95 ) ) {
mvControlled->PantFront( true );
}
if( ( false == mvControlled->PantRearUp )
&& ( ggPantRearButton.GetDesiredValue() >= 0.95 ) ) {
mvControlled->PantRear( true );
}
}
if( ggPantSelectButton.SubModel ) {
if( ggPantSelectedButton.SubModel ) {
if( ( ggPantSelectedButton.type() == TGaugeType::toggle )
&& ( ggPantSelectedButton.GetDesiredValue() >= 0.95 ) ) {
change_pantograph_selection_state( true );
}
}
else {
// HACK: force pantographs into currently selected state
change_pantograph_selection( 0, true );
}
}
}
}
*/
/*
// check whether we should raise the pantographs, based on volume in pantograph tank
// NOTE: disabled while switch state isn't preserved while moving between compartments
if( mvControlled->PantPress > (
mvControlled->TrainType == dt_EZT ?
2.4 :
3.5 ) ) {
if( ( false == mvControlled->PantFrontUp )
&& ( ggPantFrontButton.GetValue() > 0.95 ) ) {
mvControlled->PantFront( true );
}
if( ( false == mvControlled->PantRearUp )
&& ( ggPantRearButton.GetValue() > 0.95 ) ) {
mvControlled->PantRear( true );
}
}
*/
// screens
fScreenTimer += Deltatime;
if( ( fScreenTimer > Global.PythonScreenUpdateRate * 0.001f )
@@ -7718,6 +7714,7 @@ Math3D::vector3 TTrain::MirrorPosition(bool lewe)
1.5 + Cabine[iCabn].CabPos1.y,
Cabine[iCabn].CabPos1.z);
case 1: // przednia (1)
[[fallthrough]];
default:
return DynamicObject->mMatrix *
Math3D::vector3(

View File

@@ -59,13 +59,17 @@ private:
class control_mapper {
typedef std::unordered_map< TSubModel const *, std::string> submodelstring_map;
submodelstring_map m_controlnames;
using stringset = std::unordered_set<std::string>;
stringset m_names; // names of registered controls
public:
void
clear() { m_controlnames.clear(); }
clear();
void
insert( TGauge const &Gauge, std::string const &Label );
std::string
find( TSubModel const *Control ) const;
bool
contains( std::string const Control ) const;
};
class TTrain {

View File

@@ -39,6 +39,11 @@ public:
set_progress( float const Progress = 0.f, float const Subtaskprogress = 0.f ) {
if( m_userinterface != nullptr ) {
m_userinterface->set_progress( Progress, Subtaskprogress ); } }
inline
void
set_tooltip( std::string const &Tooltip ) {
if( m_userinterface != nullptr ) {
m_userinterface->set_tooltip( Tooltip ); } }
// maintenance method, called when the mode is activated
virtual
void

View File

@@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/.
#include "Globals.h"
#include "application.h"
#include "translation.h"
#include "simulation.h"
#include "simulationtime.h"
#include "simulationenvironment.h"
@@ -80,6 +81,50 @@ driver_mode::drivermode_input::init() {
return result;
}
std::string
driver_mode::drivermode_input::command_hints( std::pair<user_command, user_command> const &Commands ) const {
auto const inputhintleft { keyboard.mapping( Commands.first ) };
auto const inputhintright { keyboard.mapping( Commands.second ) };
std::string inputhints =
inputhintleft
+ ( inputhintright.empty() ? "" :
inputhintleft.empty() ?
inputhintright :
"] [" + inputhintright );
return inputhints;
}
std::unordered_map<user_command, std::pair<user_command, user_command>> commandfallbacks = {
{ user_command::mastercontrollerset, { user_command::mastercontrollerincrease, user_command::mastercontrollerdecrease } },
{ user_command::secondcontrollerset, { user_command::secondcontrollerincrease, user_command::secondcontrollerdecrease } },
{ user_command::trainbrakeset, { user_command::trainbrakeincrease, user_command::trainbrakedecrease } },
{ user_command::independentbrakeset, { user_command::independentbrakeincrease, user_command::independentbrakedecrease } },
{ user_command::linebreakeropen, { user_command::linebreakertoggle, user_command::none } },
{ user_command::linebreakerclose, { user_command::linebreakertoggle, user_command::none } },
{ user_command::pantographlowerfront, { user_command::pantographtogglefront, user_command::none } },
{ user_command::pantographlowerrear, { user_command::pantographtogglerear, user_command::none } },
};
std::pair<user_command, user_command>
driver_mode::drivermode_input::command_fallback( user_command const Command ) const {
if( Command == user_command::none ) {
return { user_command::none, user_command::none };
}
auto const lookup { commandfallbacks.find( Command ) };
if( lookup == commandfallbacks.end() ) {
return { user_command::none, user_command::none };
}
return lookup->second;
}
driver_mode::driver_mode() {
m_userinterface = std::make_shared<driver_ui>();
@@ -217,6 +262,7 @@ driver_mode::update() {
// fixed step render time routines
fTime50Hz += deltarealtime; // w pauzie też trzeba zliczać czas, bo przy dużym FPS będzie problem z odczytem ramek
bool runonce { false };
while( fTime50Hz >= 1.0 / 50.0 ) {
Console::Update(); // to i tak trzeba wywoływać
ui::Transcripts.Update(); // obiekt obsługujący stenogramy dźwięków na ekranie
@@ -232,6 +278,52 @@ driver_mode::update() {
if( std::abs( DebugCamera.Velocity.y ) < 0.01 ) { DebugCamera.Velocity.y = 0.0; }
if( std::abs( DebugCamera.Velocity.z ) < 0.01 ) { DebugCamera.Velocity.z = 0.0; }
if( false == runonce ) {
// tooltip update
set_tooltip( "" );
auto const *train{ simulation::Train };
if( ( train != nullptr ) && ( false == FreeFlyModeFlag ) ) {
if( false == DebugModeFlag ) {
// in regular mode show control functions, for defined controls
auto const controlname { train->GetLabel( GfxRenderer->Pick_Control() ) };
if( false == controlname.empty() ) {
auto const bindings { m_input.mouse.bindings( controlname ) };
auto inputhints { m_input.command_hints( bindings ) };
// if the commands bound with the control don't have any assigned keys try potential fallbacks
if( inputhints.empty() ) {
inputhints = m_input.command_hints( m_input.command_fallback( bindings.first ) );
}
if( inputhints.empty() ) {
inputhints = m_input.command_hints( m_input.command_fallback( bindings.second ) );
}
// ready or not, here we go
if( inputhints.empty() ) {
set_tooltip( locale::label_cab_control( controlname ) );
}
else {
set_tooltip(
locale::label_cab_control( controlname )
+ " [" + inputhints + "]" );
}
}
}
else {
// in debug mode show names of submodels, to help with cab setup and/or debugging
auto const cabcontrol = GfxRenderer->Pick_Control();
set_tooltip( ( cabcontrol ? cabcontrol->pName : "" ) );
}
}
if( ( true == Global.ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) {
auto const scenerynode = GfxRenderer->Pick_Node();
set_tooltip(
( scenerynode ?
scenerynode->name() :
"" ) );
}
runonce = true;
}
fTime50Hz -= 1.0 / 50.0;
}

View File

@@ -77,6 +77,10 @@ private:
bool init();
void poll();
std::string
command_hints( std::pair<user_command, user_command> const &Commands ) const;
std::pair<user_command, user_command>
command_fallback( user_command const Command ) const;
};
// methods

View File

@@ -372,70 +372,70 @@ drivermouse_input::button( int const Button, int const Action ) {
bool pickwaiting = m_pickwaiting;
m_pickwaiting = false;
auto const lookup = m_buttonbindings.find( simulation::Train->GetLabel( control ) );
if( lookup != m_buttonbindings.end() ) {
// if the recognized element under the cursor has a command associated with the pressed button, notify the recipient
mousecommand = (
Button == GLFW_MOUSE_BUTTON_LEFT ?
lookup->second.left :
lookup->second.right
);
if( mousecommand == user_command::none ) { return; }
// check manually for commands which have 'fast' variants launched with shift modifier
if( Global.shiftState ) {
switch( mousecommand ) {
case user_command::mastercontrollerincrease: { mousecommand = user_command::mastercontrollerincreasefast; break; }
case user_command::mastercontrollerdecrease: { mousecommand = user_command::mastercontrollerdecreasefast; break; }
case user_command::secondcontrollerincrease: { mousecommand = user_command::secondcontrollerincreasefast; break; }
case user_command::secondcontrollerdecrease: { mousecommand = user_command::secondcontrollerdecreasefast; break; }
case user_command::independentbrakeincrease: { mousecommand = user_command::independentbrakeincreasefast; break; }
case user_command::independentbrakedecrease: { mousecommand = user_command::independentbrakedecreasefast; break; }
default: { break; }
}
auto const controlbindings { bindings( simulation::Train->GetLabel( control ) ) };
// if the recognized element under the cursor has a command associated with the pressed button, notify the recipient
mousecommand = (
Button == GLFW_MOUSE_BUTTON_LEFT ?
controlbindings.first :
controlbindings.second
);
if( mousecommand == user_command::none ) {
// if we don't have any recognized element under the cursor and the right button was pressed, enter view panning mode
if( Button == GLFW_MOUSE_BUTTON_RIGHT ) {
m_pickmodepanning = true;
}
return;
}
// check manually for commands which have 'fast' variants launched with shift modifier
if( Global.shiftState ) {
switch( mousecommand ) {
case user_command::mastercontrollerincrease: { mousecommand = user_command::mastercontrollerincreasefast; break; }
case user_command::mastercontrollerdecrease: { mousecommand = user_command::mastercontrollerdecreasefast; break; }
case user_command::secondcontrollerincrease: { mousecommand = user_command::secondcontrollerincreasefast; break; }
case user_command::secondcontrollerdecrease: { mousecommand = user_command::secondcontrollerdecreasefast; break; }
case user_command::independentbrakeincrease: { mousecommand = user_command::independentbrakeincreasefast; break; }
case user_command::independentbrakedecrease: { mousecommand = user_command::independentbrakedecreasefast; break; }
default: { break; }
}
}
switch( mousecommand ) {
case user_command::mastercontrollerincrease:
case user_command::mastercontrollerdecrease:
case user_command::secondcontrollerincrease:
case user_command::secondcontrollerdecrease:
case user_command::trainbrakeincrease:
case user_command::trainbrakedecrease:
case user_command::independentbrakeincrease:
case user_command::independentbrakedecrease: {
// these commands trigger varying repeat rate mode,
// which scales the rate based on the distance of the cursor from its point when the command was first issued
m_varyingpollrateorigin = m_cursorposition;
m_varyingpollrate = true;
break;
}
case user_command::jointcontrollerset:
case user_command::mastercontrollerset:
case user_command::secondcontrollerset:
case user_command::trainbrakeset:
case user_command::independentbrakeset: {
m_slider.bind( mousecommand );
mousecommand = user_command::none;
return;
}
default: {
break;
}
switch( mousecommand ) {
case user_command::mastercontrollerincrease:
case user_command::mastercontrollerdecrease:
case user_command::secondcontrollerincrease:
case user_command::secondcontrollerdecrease:
case user_command::trainbrakeincrease:
case user_command::trainbrakedecrease:
case user_command::independentbrakeincrease:
case user_command::independentbrakedecrease: {
// these commands trigger varying repeat rate mode,
// which scales the rate based on the distance of the cursor from its point when the command was first issued
m_varyingpollrateorigin = m_cursorposition;
m_varyingpollrate = true;
break;
}
// NOTE: basic keyboard controls don't have any parameters
// NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( mousecommand, 0, 0, Action, 0 );
if (!pickwaiting) // already depressed
m_relay.post( mousecommand, 0, 0, GLFW_RELEASE, 0 );
m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one
}
else {
// if we don't have any recognized element under the cursor and the right button was pressed, enter view panning mode
if( Button == GLFW_MOUSE_BUTTON_RIGHT ) {
m_pickmodepanning = true;
} } } );
case user_command::jointcontrollerset:
case user_command::mastercontrollerset:
case user_command::secondcontrollerset:
case user_command::trainbrakeset:
case user_command::independentbrakeset: {
m_slider.bind( mousecommand );
mousecommand = user_command::none;
return;
}
default: {
break;
}
}
// NOTE: basic keyboard controls don't have any parameters
// NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( mousecommand, 0, 0, Action, 0 );
if (!pickwaiting) // already depressed
m_relay.post( mousecommand, 0, 0, GLFW_RELEASE, 0 );
m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one
} );
}
}
}
@@ -495,6 +495,19 @@ drivermouse_input::command() const {
m_mousecommandright );
}
// returns pair of bindings associated with specified cab control
std::pair<user_command, user_command>
drivermouse_input::bindings( std::string const &Control ) const {
auto const lookup{ m_buttonbindings.find( Control ) };
if( lookup != m_buttonbindings.end() )
return { lookup->second.left, lookup->second.right };
else {
return { user_command::none, user_command::none };
}
}
void
drivermouse_input::default_bindings() {

View File

@@ -66,6 +66,9 @@ public:
poll();
user_command
command() const;
// returns pair of bindings associated with specified cab control
std::pair<user_command, user_command>
bindings( std::string const &Control ) const;
private:
// types

View File

@@ -164,29 +164,6 @@ driver_ui::update() {
}
m_paused = ispaused;
set_tooltip( "" );
auto const *train { simulation::Train };
if( ( train != nullptr ) && ( false == FreeFlyModeFlag ) ) {
if( false == DebugModeFlag ) {
// in regular mode show control functions, for defined controls
set_tooltip( locale::label_cab_control( train->GetLabel( GfxRenderer->Pick_Control() ) ) );
}
else {
// in debug mode show names of submodels, to help with cab setup and/or debugging
auto const cabcontrol = GfxRenderer->Pick_Control();
set_tooltip( ( cabcontrol ? cabcontrol->pName : "" ) );
}
}
if( ( true == Global.ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) {
auto const scenerynode = GfxRenderer->Pick_Node();
set_tooltip(
( scenerynode ?
scenerynode->name() :
"" ) );
}
ui_layer::update();
}

View File

@@ -542,7 +542,25 @@ debug_panel::render() {
render_section( "Vehicle Engine", m_enginelines );
render_section( "Vehicle AI", m_ailines );
render_section( "Vehicle Scan Table", m_scantablelines );
render_section( "Scenario", m_scenariolines );
if( true == render_section( "Scenario", m_scenariolines ) ) {
// cloud cover slider
if( ImGui::SliderFloat(
( to_string(Global.Overcast, 2 ) + " (" + Global.Weather + ")###overcast" ).c_str(), &Global.Overcast, 0.0f, 2.0f, "Cloud cover" ) ) {
Global.Overcast = clamp( Global.Overcast, 0.0f, 2.0f );
simulation::Environment.compute_weather();
}
// day of year slider
if( ImGui::SliderFloat( ( to_string( Global.fMoveLight, 0, 4 ) + " (" + Global.Season + ")###movelight" ).c_str(), &Global.fMoveLight, 0.0f, 355.0f, "Day of year" ) ) {
Global.fMoveLight = clamp( Global.fMoveLight, 0.0f, 355.0f );
auto const weather { Global.Weather };
simulation::Environment.compute_season( Global.fMoveLight );
simulation::Time.init();
if( weather != Global.Weather ) {
// HACK: force re-calculation of precipitation
Global.Overcast = clamp( Global.Overcast - 0.0001f, 0.0f, 2.0f );
}
}
}
if( true == render_section( "Scenario Event Queue", m_eventqueuelines ) ) {
// event queue filter
ImGui::Checkbox( "By This Vehicle Only", &m_eventqueueactivevehicleonly );
@@ -597,8 +615,8 @@ debug_panel::update_section_vehicle( std::vector<text_line> &Output ) {
( mover.Mains ? 'M' : '.' ),
( mover.FuseFlag ? '!' : '.' ),
( mover.PantsValve.is_active ? '+' : '.' ),
( mover.Pantographs[ end::rear ].valve.is_enabled ? ( mover.Pantographs[ end::rear ].valve.is_active ? 'O' : 'o' ) : '.' ),
( mover.Pantographs[end::front].valve.is_enabled ? ( mover.Pantographs[ end::front ].valve.is_active ? 'P' : 'p' ) : '.' ),
( mover.Pantographs[ end::rear ].valve.is_active ? 'O' : ( mover.Pantographs[ end::rear ].valve.is_enabled ? 'o' : '.' ) ),
( mover.Pantographs[ end::front ].valve.is_active ? 'P' : ( mover.Pantographs[ end::front ].valve.is_enabled ? 'p' : '.' ) ),
( mover.PantPressLockActive ? '!' : ( mover.PantPressSwitchActive ? '*' : '.' ) ),
( mover.WaterPump.is_active ? 'W' : ( false == mover.WaterPump.breaker ? '-' : ( mover.WaterPump.is_enabled ? 'w' : '.' ) ) ),
( true == mover.WaterHeater.is_damaged ? '!' : ( mover.WaterHeater.is_active ? 'H' : ( false == mover.WaterHeater.breaker ? '-' : ( mover.WaterHeater.is_enabled ? 'h' : '.' ) ) ) ),
@@ -987,8 +1005,7 @@ debug_panel::update_section_scenario( std::vector<text_line> &Output ) {
Output.emplace_back( textline, Global.UITextColor );
// current luminance level
textline = "Cloud cover: " + to_string( Global.Overcast, 3 );
textline += "\nLight level: " + to_string( Global.fLuminance, 3 );
textline = "Light level: " + to_string( Global.fLuminance, 3 ) + ( Global.FakeLight ? "(*)" : "" );
if( Global.FakeLight ) { textline += "(*)"; }
textline +=
"\nWind: azimuth "

View File

@@ -159,8 +159,8 @@ keyboard_input::key( int const Key, int const Action ) {
| ( modifier ? 0 : ( input::key_shift ? keymodifier::shift : 0 ) )
| ( modifier ? 0 : ( input::key_ctrl ? keymodifier::control : 0 ) );
auto const lookup = m_bindings.find( key );
if( lookup == m_bindings.end() ) {
auto const lookup = m_commands.find( key );
if( lookup == m_commands.end() ) {
// no binding for this key
return false;
}
@@ -187,7 +187,8 @@ keyboard_input::bind() {
for( auto const &bindingsetup : m_bindingsetups ) {
m_bindings[ bindingsetup.binding ] = bindingsetup.command;
m_commands[ bindingsetup.binding ] = bindingsetup.command;
m_bindings[ bindingsetup.command ] = bindingsetup.binding;
}
// cache movement key bindings
m_bindingscache.forward = binding( user_command::moveforward );
@@ -201,12 +202,11 @@ keyboard_input::bind() {
int
keyboard_input::binding( user_command const Command ) const {
for( auto const &binding : m_bindings ) {
if( binding.second == Command ) {
return binding.first;
}
}
return -1;
auto const lookup{ m_bindings.find( Command ) };
return (
lookup != m_bindings.end() ?
lookup->second :
-1 );
}
bool
@@ -274,4 +274,50 @@ keyboard_input::poll() {
m_movementvertical = movementvertical;
}
std::unordered_map<int, std::string> keytonamemap = {
{ GLFW_KEY_0, "0" }, { GLFW_KEY_1, "1" }, { GLFW_KEY_2, "2" }, { GLFW_KEY_3, "3" }, { GLFW_KEY_4, "4" },
{ GLFW_KEY_5, "5" }, { GLFW_KEY_6, "6" }, { GLFW_KEY_7, "7" }, { GLFW_KEY_8, "8" }, { GLFW_KEY_9, "9" },
{ GLFW_KEY_MINUS, "-" }, { GLFW_KEY_EQUAL, "=" },
{ GLFW_KEY_A, "A" }, { GLFW_KEY_B, "B" }, { GLFW_KEY_C, "C" }, { GLFW_KEY_D, "D" }, { GLFW_KEY_E, "E" },
{ GLFW_KEY_F, "F" }, { GLFW_KEY_G, "G" }, { GLFW_KEY_H, "H" }, { GLFW_KEY_I, "I" }, { GLFW_KEY_J, "J" },
{ GLFW_KEY_K, "K" }, { GLFW_KEY_L, "L" }, { GLFW_KEY_M, "M" }, { GLFW_KEY_N, "N" }, { GLFW_KEY_O, "O" },
{ GLFW_KEY_P, "P" }, { GLFW_KEY_Q, "Q" }, { GLFW_KEY_R, "R" }, { GLFW_KEY_S, "S" }, { GLFW_KEY_T, "T" },
{ GLFW_KEY_U, "U" }, { GLFW_KEY_V, "V" }, { GLFW_KEY_W, "W" }, { GLFW_KEY_X, "X" }, { GLFW_KEY_Y, "Y" }, { GLFW_KEY_Z, "Z" },
{ GLFW_KEY_BACKSPACE, "BACKSPACE" },
{ GLFW_KEY_LEFT_BRACKET, "[" }, { GLFW_KEY_RIGHT_BRACKET, "]" }, { GLFW_KEY_BACKSLASH, "\\" },
{ GLFW_KEY_SEMICOLON, ";" }, { GLFW_KEY_APOSTROPHE, "'" }, { GLFW_KEY_ENTER, "ENTER" },
{ GLFW_KEY_COMMA, "<" }, { GLFW_KEY_PERIOD, ">" }, { GLFW_KEY_SLASH, "/" },
{ GLFW_KEY_SPACE, "SPACE" },
{ GLFW_KEY_PAUSE, "PAUSE" }, { GLFW_KEY_INSERT, "INSERT" }, { GLFW_KEY_DELETE, "DELETE" }, { GLFW_KEY_HOME, "HOME" }, { GLFW_KEY_END, "END" },
// numpad block
{ GLFW_KEY_KP_DIVIDE, "NUM /" }, { GLFW_KEY_KP_MULTIPLY, "NUM *" }, { GLFW_KEY_KP_SUBTRACT, "NUM -" },
{ GLFW_KEY_KP_7, "NUM 7" }, { GLFW_KEY_KP_8, "NUM 8" }, { GLFW_KEY_KP_9, "NUM 9" }, { GLFW_KEY_KP_ADD, "NUM +" },
{ GLFW_KEY_KP_4, "NUM 4" }, { GLFW_KEY_KP_5, "NUM 5" }, { GLFW_KEY_KP_6, "NUM 6" },
{ GLFW_KEY_KP_1, "NUM 1" }, { GLFW_KEY_KP_2, "NUM 2" }, { GLFW_KEY_KP_3, "NUM 3" }, { GLFW_KEY_KP_ENTER, "NUM ENTER" },
{ GLFW_KEY_KP_0, "NUM 0" }, { GLFW_KEY_KP_DECIMAL, "NUM ." }
};
std::string
keyboard_input::mapping( user_command const Command ) const {
if( Command == user_command::none ) { return ""; }
auto const binding { this->binding( Command ) };
if( binding == -1 ) { return ""; }
auto const lookup { keytonamemap.find( binding & 0xffff ) };
if( lookup == keytonamemap.end() ) { return ""; }
std::string mapping;
if( ( binding & keymodifier::shift ) != 0 ) {
mapping += "SHIFT ";
}
if( ( binding & keymodifier::control ) != 0 ) {
mapping += "CTRL ";
}
mapping += lookup->second;
return mapping;
}
//---------------------------------------------------------------------------

View File

@@ -45,6 +45,8 @@ public:
user_command const
command() const {
return m_command; }
std::string
mapping( user_command const Command ) const;
protected:
// types
@@ -77,6 +79,7 @@ protected:
private:
// types
using usercommand_map = std::unordered_map<int, user_command>;
using binding_map = std::unordered_map<user_command, int>;
struct bindings_cache {
@@ -96,7 +99,8 @@ private:
// members
user_command m_command { user_command::none }; // last, if any, issued command
usercommand_map m_bindings;
usercommand_map m_commands;
binding_map m_bindings;
command_relay m_relay;
bindings_cache m_bindingscache;
glm::vec2 m_movementhorizontal { 0.f };

View File

@@ -17,6 +17,8 @@ http://mozilla.org/MPL/2.0/.
#include "Globals.h"
#include "Logs.h"
opengl_material::path_data opengl_material::paths;
opengl_material::opengl_material()
{
for (size_t i = 0; i < params.size(); i++)
@@ -32,6 +34,21 @@ opengl_material::deserialize( cParser &Input, bool const Loadnow ) {
result = true; // once would suffice but, eh
}
if( ( path == -1 )
&& ( update_on_weather_change || update_on_season_change ) ) {
// record current texture path in the material, potentially needed when material is reloaded on environment change
// NOTE: we're storing this only for textures that can actually change, to keep the size of path database modest
auto const lookup{ paths.index_map.find( Global.asCurrentTexturePath ) };
if( lookup != paths.index_map.end() ) {
path = lookup->second;
}
else {
path = paths.data.size();
paths.data.emplace_back( Global.asCurrentTexturePath );
paths.index_map.emplace( Global.asCurrentTexturePath, path );
}
}
return result;
}
@@ -94,19 +111,20 @@ void opengl_material::finalize(bool Loadnow)
if (!shader)
{
// TODO: add error severity to logging, re-enable these errors as low severity messages
if (textures[0] == null_handle)
{
log_error("shader not specified, assuming \"default_0\"");
// log_error("shader not specified, assuming \"default_0\"");
shader = GfxRenderer->Fetch_Shader("default_0");
}
else if (textures[1] == null_handle)
{
log_error("shader not specified, assuming \"default_1\"");
// log_error("shader not specified, assuming \"default_1\"");
shader = GfxRenderer->Fetch_Shader("default_1");
}
else if (textures[2] == null_handle)
{
log_error("shader not specified, assuming \"default_2\"");
// log_error("shader not specified, assuming \"default_2\"");
shader = GfxRenderer->Fetch_Shader("default_2");
}
}
@@ -198,6 +216,55 @@ void opengl_material::finalize(bool Loadnow)
}
}
bool opengl_material::update() {
auto const texturepathbackup { Global.asCurrentTexturePath };
auto const namebackup { name };
auto const pathbackup { path };
cParser materialparser( name + ".mat", cParser::buffer_FILE ); // fairly safe to presume .mat is present for branching materials
// temporarily set texture path to state recorded in the material
Global.asCurrentTexturePath = paths.data[ path ];
// clean material slate, restore relevant members
*this = opengl_material();
name = namebackup;
path = pathbackup;
auto result { false };
if( true == deserialize( materialparser, true ) ) {
try {
finalize( true );
result = true;
}
catch( gl::shader_exception const &e ) {
ErrorLog( "invalid shader: " + std::string( e.what() ) );
}
}
// restore texture path
Global.asCurrentTexturePath = texturepathbackup;
return result;
}
std::unordered_set<std::string> seasons = {
"winter:", "spring:", "summer:", "autumn:"
};
bool is_season( std::string const &String ) {
return ( seasons.find( String ) != seasons.end() );
}
std::unordered_set<std::string> weather = {
"clear:", "cloudy:", "rain:", "snow:" };
bool is_weather( std::string const &String ) {
return ( weather.find( String ) != weather.end() );
}
// imports member data pair from the config file
bool
opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow ) {
@@ -209,6 +276,11 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c
if( Priority != -1 ) {
// regular attribute processing mode
// mark potential material change
update_on_weather_change |= is_weather( key );
update_on_season_change |= is_season( key );
if( key == Global.Weather ) {
// weather textures override generic (pri 0) and seasonal (pri 1) textures
// seasonal weather textures (pri 1+2=3) override generic weather (pri 2) textures
@@ -433,14 +505,13 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
if( false == material.name.empty() ) {
// if we have material name and shader it means resource was processed succesfully
try {
materialhandle = m_materials.size();
m_materialmappings.emplace( material.name, materialhandle );
try {
material.finalize(Loadnow);
materialhandle = m_materials.size();
m_materialmappings.emplace( material.name, materialhandle );
m_materials.emplace_back( std::move(material) );
} catch (gl::shader_exception const &e) {
ErrorLog("invalid shader: " + std::string(e.what()));
m_materialmappings.emplace( filename, materialhandle );
}
}
else {
@@ -451,6 +522,22 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
return materialhandle;
};
void
material_manager::on_weather_change() {
for( auto &material : m_materials ) {
if( material.update_on_weather_change ) { material.update(); }
}
}
void
material_manager::on_season_change() {
for( auto &material : m_materials ) {
if( material.update_on_season_change ) { material.update(); }
}
}
// checks whether specified material is in the material bank. returns handle to the material, or a null handle
material_handle
material_manager::find_in_databank( std::string const &Materialname ) const {

View File

@@ -38,10 +38,18 @@ struct opengl_material {
bool
deserialize( cParser &Input, bool const Loadnow );
void finalize(bool Loadnow);
bool update();
float get_or_guess_opacity() const;
bool is_translucent() const;
// members
static struct path_data {
std::unordered_map<std::string, int> index_map;
std::vector<std::string> data;
} paths;
bool is_good { false }; // indicates material was compiled without failure
int path{ -1 }; // index to material path
bool update_on_weather_change{ false };
bool update_on_season_change{ false };
private:
// methods
@@ -49,9 +57,6 @@ private:
bool
deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow );
void log_error(const std::string &str);
// extracts name of the sound file from provided data stream
std::string
deserialize_filename( cParser &Input );
// members
// priorities for textures, shader, opacity
@@ -89,6 +94,12 @@ public:
material( material_handle const Material ) const { return m_materials[ Material ]; }
opengl_material &
material( material_handle const Material ) { return m_materials[ Material ]; }
// material updates executed when environment changes
// TODO: registerable callbacks in environment manager
void
on_weather_change();
void
on_season_change();
private:
// types

View File

@@ -113,7 +113,7 @@ OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
if (*pRozkaz->iPar == 0) // sprawdzenie czasu
if (*pRozkaz->iPar & 1) // ustawienie czasu
{
double t = pRozkaz->fPar[1];
auto t = pRozkaz->fPar[1];
simulation::Time.data().wDay = std::floor(t); // niby nie powinno być dnia, ale...
if (Global.fMoveLight >= 0)
Global.fMoveLight = t; // trzeba by deklinację Słońca przeliczyć

View File

@@ -1597,9 +1597,14 @@ bool opengl33_renderer::Render(world_environment *Environment)
if (Environment->m_clouds.mdCloud)
{
// setup
glm::vec3 color = interpolate(Environment->m_skydome.GetAverageColor(), suncolor, duskfactor * 0.25f) * interpolate(1.f, 0.35f, Global.Overcast / 2.f) // overcast darkens the clouds
* 0.5f;
glm::vec3 color =
interpolate(
Environment->m_skydome.GetAverageColor(), suncolor,
duskfactor * 0.25f)
* interpolate(
1.f, 0.35f,
Global.Overcast / 2.f) // overcast darkens the clouds
* 0.5f;
// write cloud color into material
TSubModel *mdl = Environment->m_clouds.mdCloud->Root;
if (mdl->m_material != null_handle)
@@ -1751,17 +1756,18 @@ void opengl33_renderer::Bind_Material(material_handle const Material, TSubModel
model_ubs.param[entry.location][entry.offset + j] = src[j];
}
if (m_blendingenabled)
{
model_ubs.opacity = -1.0f;
}
else
{
if (!std::isnan(material.opacity))
model_ubs.opacity = material.opacity;
else
model_ubs.opacity = 0.5f;
}
if( !std::isnan( material.opacity ) ) {
model_ubs.opacity = (
m_blendingenabled ?
-material.opacity :
material.opacity );
}
else {
model_ubs.opacity = (
m_blendingenabled ?
0.0f :
0.5f );
}
if (sm)
model_ubs.alpha_mult = sm->fVisible;
@@ -3926,6 +3932,19 @@ void opengl33_renderer::Update(double const Deltatime)
}
}
// update resources if there was environmental change
simulation_state simulationstate {
Global.Weather,
Global.Season
};
std::swap( m_simulationstate, simulationstate );
if( ( m_simulationstate.season != simulationstate.season ) && ( false == simulationstate.season.empty() ) ) {
m_materials.on_season_change();
}
if( ( m_simulationstate.weather != simulationstate.weather ) && ( false == simulationstate.weather.empty() ) ) {
m_materials.on_weather_change();
}
if ((true == Global.ResourceSweep) && (true == simulation::is_ready))
{
// garbage collection

View File

@@ -300,6 +300,10 @@ class opengl33_renderer : public gfx_renderer {
std::string m_pickdebuginfo;
//debug_stats m_debugstats;
std::string m_debugstatstext;
struct simulation_state {
std::string weather;
std::string season;
} m_simulationstate;
glm::vec4 m_baseambient{0.0f, 0.0f, 0.0f, 1.0f};
glm::vec4 m_shadowcolor{colors::shadow};

View File

@@ -3601,14 +3601,14 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
#endif
// material configuration:
// textures...
if( Submodel->m_material < 0 ) { // zmienialne skóry
Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] );
}
else {
// również 0
Bind_Material( Submodel->m_material );
}
// ...colors...
auto const material { (
Submodel->m_material < 0 ?
Submodel->ReplacableSkinId[ -Submodel->m_material ] : // zmienialne skóry
Submodel->m_material ) }; // również 0
// textures...
Bind_Material( material );
// ...colors and opacity...
auto const opacity { clamp( Material( material ).get_or_guess_opacity(), 0.f, 1.f ) };
if( Submodel->fVisible < 1.f ) {
::glColor4f(
Submodel->f4Diffuse.r,
@@ -3619,6 +3619,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
else {
::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub
}
if( opacity != 0.f ) {
// discard fragments with opacity exceeding the threshold
::glAlphaFunc( GL_LEQUAL, opacity );
}
if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) {
::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( Submodel->f4Specular * m_sunlight.specular.a * m_speculartranslucentscalefactor ) );
}
@@ -3639,6 +3643,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
m_geometry.draw( Submodel->m_geometry );
// post-draw reset
if( opacity != 0.f ) {
// discard fragments with opacity exceeding the threshold
::glAlphaFunc( GL_GREATER, 0.0f );
}
if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) {
::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) );
}
@@ -4032,6 +4040,19 @@ opengl_renderer::Update( double const Deltatime ) {
}
}
// update resources if there was environmental change
simulation_state simulationstate {
Global.Weather,
Global.Season
};
std::swap( m_simulationstate, simulationstate );
if( ( m_simulationstate.season != simulationstate.season ) && ( false == simulationstate.season.empty() ) ) {
m_materials.on_season_change();
}
if( ( m_simulationstate.weather != simulationstate.weather ) && ( false == simulationstate.weather.empty() ) ) {
m_materials.on_weather_change();
}
if( ( true == Global.ResourceSweep )
&& ( true == simulation::is_ready ) ) {
// garbage collection

View File

@@ -320,6 +320,10 @@ private:
std::string m_debugtimestext;
std::string m_pickdebuginfo;
std::string m_debugstatstext;
struct simulation_state {
std::string weather;
std::string season;
} m_simulationstate;
glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f };
glm::vec4 m_shadowcolor { colors::shadow };

View File

@@ -169,15 +169,16 @@ world_environment::update() {
Global.FogColor = m_skydome.GetAverageHorizonColor();
// weather-related simulation factors
// TODO: dynamic change of air temperature and overcast levels
Global.FrictionWeatherFactor = (
Global.Weather == "rain:" ? 0.85f :
Global.Weather == "snow:" ? 0.75f :
1.0f );
if( Global.Weather == "rain:" ) {
// reduce friction in rain
Global.FrictionWeatherFactor = 0.85f;
m_precipitationsound.play( sound_flags::exclusive | sound_flags::looping );
}
else if( Global.Weather == "snow:" ) {
// reduce friction due to snow
Global.FrictionWeatherFactor = 0.75f;
else {
m_precipitationsound.stop();
}
update_wind();

View File

@@ -34,7 +34,7 @@ scenario_time::init() {
// cache requested elements, if any
::GetLocalTime( &m_time );
if( Global.fMoveLight > 0.0 ) {
if( Global.fMoveLight > 0.f ) {
// day and month of the year can be overriden by scenario setup
daymonth( m_time.wDay, m_time.wMonth, m_time.wYear, static_cast<WORD>( Global.fMoveLight ) );
}

View File

@@ -117,20 +117,23 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
}
if( Legacyparameters & sound_parameters::range ) {
Input.getTokens( 1, false );
Input >> m_range;
if( Input.getTokens( 1, false ) ) {
Input >> m_range;
}
}
if( Legacyparameters & sound_parameters::amplitude ) {
Input.getTokens( 2, false );
Input
>> m_amplitudefactor
>> m_amplitudeoffset;
if( Input.getTokens( 2, false ) ) {
Input
>> m_amplitudefactor
>> m_amplitudeoffset;
}
}
if( Legacyparameters & sound_parameters::frequency ) {
Input.getTokens( 2, false );
Input
>> m_frequencyfactor
>> m_frequencyoffset;
if( Input.getTokens( 2, false ) ) {
Input
>> m_frequencyfactor
>> m_frequencyoffset;
}
}
}
// restore parser behaviour

View File

@@ -85,6 +85,20 @@ init() {
"second controller",
"shunt mode power",
"tempomat",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (power)",
"tempomat (power)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"tempomat (speed)",
"distance counter",
"reverser",
"train brake",
@@ -114,6 +128,9 @@ init() {
"spring brake",
"spring brake",
"spring brake",
"train brake",
"train brake",
"train brake",
"electro-pneumatic brake",
"sandbox",
"wheelspin brake",
@@ -163,6 +180,9 @@ init() {
"radiostop test",
"radiostop",
"radio call 3",
"radio volume",
"radio volume",
"radio volume",
"pantograph A",
"pantograph B",
"pantograph A",
@@ -260,6 +280,20 @@ init() {
"nastawnik dodatkowy",
"sterowanie analogowe",
"tempomat",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (moc)",
"tempomat (moc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"tempomat (predkosc)",
"miernik odleglosci",
"nastawnik kierunku",
"hamulec zespolony",
@@ -289,6 +323,9 @@ init() {
"hamulec sprezynowy",
"hamulec sprezynowy",
"hamulec sprezynowy",
"hamulec zespolony",
"hamulec zespolony",
"hamulec zespolony",
"hamulec elektropneumatyczny",
"piasecznica",
"hamulec przeciwposlizgowy",
@@ -338,6 +375,9 @@ init() {
"test radiostopu",
"radiostop",
"zew 3",
"glosnosc radia",
"glosnosc radia",
"glosnosc radia",
"pantograf A",
"pantograf B",
"pantograf A",
@@ -388,6 +428,20 @@ init() {
"scndctrl:",
"shuntmodepower:",
"tempomat_sw:",
"speedinc_bt:",
"speeddec_bt:",
"speedctrlpowerinc_bt:",
"speedctrlpowerdec_bt:",
"speedbutton0:",
"speedbutton1:",
"speedbutton2:",
"speedbutton3:",
"speedbutton4:",
"speedbutton5:",
"speedbutton6:",
"speedbutton7:",
"speedbutton8:",
"speedbutton9:",
"distancecounter_sw:",
"dirkey:",
"brakectrl:",
@@ -417,6 +471,9 @@ init() {
"springbraketoggle_bt:",
"springbrakeon_bt:",
"springbrakeoff_bt:",
"universalbrake1_bt:",
"universalbrake2_bt:",
"universalbrake3_bt:",
"epbrake_bt:",
"sand_bt:",
"antislip_bt:",
@@ -466,6 +523,9 @@ init() {
"radiotest_sw:",
"radiostop_sw:",
"radiocall3_sw:",
"radiovolume_sw:",
"radiovolumeprev_sw:",
"radiovolumenext_sw:",
"pantfront_sw:",
"pantrear_sw:",
"pantfrontoff_sw:",

View File

@@ -74,6 +74,20 @@ enum string {
cab_scndctrl,
cab_shuntmodepower,
cab_tempomat,
cab_speedinc,
cab_speeddec,
cab_speedctrlpowerinc,
cab_speedctrlpowerdec,
cab_speedbutton0,
cab_speedbutton1,
cab_speedbutton2,
cab_speedbutton3,
cab_speedbutton4,
cab_speedbutton5,
cab_speedbutton6,
cab_speedbutton7,
cab_speedbutton8,
cab_speedbutton9,
cab_distancecounter,
cab_dirkey,
cab_brakectrl,
@@ -103,6 +117,9 @@ enum string {
cab_springbrake_bt,
cab_springbrakeon_bt,
cab_springbrakeoff_bt,
cab_universalbrake1_bt,
cab_universalbrake2_bt,
cab_universalbrake3_bt,
cab_epbrake_bt,
cab_sand_bt,
cab_antislip_bt,
@@ -152,6 +169,9 @@ enum string {
cab_radiotest_sw,
cab_radiostop_sw,
cab_radiocall3_sw,
cab_radiovolume_sw,
cab_radiovolumeprev_sw,
cab_radiovolumenext_sw,
cab_pantfront_sw,
cab_pantrear_sw,
cab_pantfrontoff_sw,

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 20
#define VERSION_MINOR 120
#define VERSION_MINOR 124
#define VERSION_REVISION 0